Bug 1028497 - Part 25: Support loading of fonts from ArrayBuffer{,View}s. r=bzbarsky,jdaggett

This commit is contained in:
Cameron McCormack 2014-10-02 12:32:09 +10:00
Родитель 1ea00788ea
Коммит 103bb77e68
5 изменённых файлов: 300 добавлений и 100 удалений

Просмотреть файл

@ -251,11 +251,17 @@ gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
gfxUserFontData* userFontData = aFontEntry->mUserFontData;
userFontData->mSrcIndex = mSrcIndex;
const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
if (src.mIsLocal) {
userFontData->mLocalName = src.mLocalName;
} else {
userFontData->mURI = src.mURI;
userFontData->mPrincipal = mPrincipal;
switch (src.mSourceType) {
case gfxFontFaceSrc::eSourceType_Local:
userFontData->mLocalName = src.mLocalName;
break;
case gfxFontFaceSrc::eSourceType_URL:
userFontData->mURI = src.mURI;
userFontData->mPrincipal = mPrincipal;
break;
case gfxFontFaceSrc::eSourceType_Buffer:
userFontData->mIsBuffer = true;
break;
}
userFontData->mPrivate = aPrivate;
userFontData->mFormat = src.mFormatFlags;
@ -357,11 +363,11 @@ gfxUserFontEntry::LoadNextSrc()
// load each src entry in turn, until a local face is found
// or a download begins successfully
while (mSrcIndex < numSrc) {
const gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
// src local ==> lookup and load immediately
if (currSrc.mIsLocal) {
if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
gfxFontEntry* fe =
gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
mWeight,
@ -393,7 +399,7 @@ gfxUserFontEntry::LoadNextSrc()
}
// src url ==> start the load process
else {
else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
currSrc.mFormatFlags)) {
@ -480,6 +486,28 @@ gfxUserFontEntry::LoadNextSrc()
}
}
// FontFace buffer ==> load immediately
else {
MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer);
uint8_t* buffer = nullptr;
uint32_t bufferLength = 0;
// sync load font immediately
currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
if (buffer && LoadPlatformFont(buffer, bufferLength)) {
// LoadPlatformFont takes ownership of the buffer, so no need
// to free it here.
SetLoadState(STATUS_LOADED);
return;
} else {
mFontSet->LogMessage(this,
"font load failed",
nsIScriptError::errorFlag);
}
}
mSrcIndex++;
}
@ -597,7 +625,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
// The downloaded data can now be discarded; the font entry is using the
// sanitized copy
NS_Free((void*)aFontData);
moz_free((void*)aFontData);
return fe != nullptr;
}
@ -612,7 +640,7 @@ gfxUserFontEntry::Load()
// This is called when a font download finishes.
// Ownership of aFontData passes in here, and the font set must
// ensure that it is eventually deleted via NS_Free().
// ensure that it is eventually deleted via moz_free().
bool
gfxUserFontEntry::FontDataDownloadComplete(const uint8_t* aFontData,
uint32_t aLength,
@ -1008,6 +1036,16 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry,
{
NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
"caching a font associated with no family yet");
gfxUserFontData* data = aFontEntry->mUserFontData;
if (data->mIsBuffer) {
#ifdef DEBUG_USERFONT_CACHE
printf("userfontcache skipped fontentry with buffer source: %p\n",
aFontEntry);
#endif
return;
}
if (!sUserFonts) {
sUserFonts = new nsTHashtable<Entry>;
@ -1022,7 +1060,6 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry,
}
}
gfxUserFontData* data = aFontEntry->mUserFontData;
if (data->mLength) {
MOZ_ASSERT(aPersistence == kPersistent);
MOZ_ASSERT(!data->mPrivate);

Просмотреть файл

@ -19,10 +19,27 @@ class nsFontFaceLoader;
//#define DEBUG_USERFONT_CACHE
class gfxFontFaceBufferSource
{
NS_INLINE_DECL_REFCOUNTING(gfxFontFaceBufferSource)
public:
virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) = 0;
protected:
virtual ~gfxFontFaceBufferSource() {}
};
// parsed CSS @font-face rule information
// lifetime: from when @font-face rule processed until font is loaded
struct gfxFontFaceSrc {
bool mIsLocal; // url or local
enum SourceType {
eSourceType_Local,
eSourceType_URL,
eSourceType_Buffer
};
SourceType mSourceType;
// if url, whether to use the origin principal or not
bool mUseOriginPrincipal;
@ -36,20 +53,33 @@ struct gfxFontFaceSrc {
nsCOMPtr<nsIURI> mURI; // uri if url
nsCOMPtr<nsIURI> mReferrer; // referrer url if url
nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url
nsRefPtr<gfxFontFaceBufferSource> mBuffer;
};
inline bool
operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b)
{
bool equals;
return (a.mIsLocal && b.mIsLocal &&
a.mLocalName == b.mLocalName) ||
(!a.mIsLocal && !b.mIsLocal &&
a.mUseOriginPrincipal == b.mUseOriginPrincipal &&
a.mFormatFlags == b.mFormatFlags &&
NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals &&
NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) && equals &&
a.mOriginPrincipal->Equals(b.mOriginPrincipal));
if (a.mSourceType != b.mSourceType) {
return false;
}
switch (a.mSourceType) {
case gfxFontFaceSrc::eSourceType_Local:
return a.mLocalName == b.mLocalName;
case gfxFontFaceSrc::eSourceType_URL: {
bool equals;
return a.mUseOriginPrincipal == b.mUseOriginPrincipal &&
a.mFormatFlags == b.mFormatFlags &&
NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals &&
NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) &&
equals &&
a.mOriginPrincipal->Equals(b.mOriginPrincipal);
}
case gfxFontFaceSrc::eSourceType_Buffer:
return a.mBuffer == b.mBuffer;
}
NS_WARNING("unexpected mSourceType");
return false;
}
// Subclassed to store platform-specific code cleaned out when font entry is
@ -62,7 +92,7 @@ class gfxUserFontData {
public:
gfxUserFontData()
: mSrcIndex(0), mFormat(0), mMetaOrigLen(0),
mCRC32(0), mLength(0), mPrivate(false)
mCRC32(0), mLength(0), mPrivate(false), mIsBuffer(false)
{ }
virtual ~gfxUserFontData() { }
@ -77,6 +107,7 @@ public:
uint32_t mCRC32; // Checksum
uint32_t mLength; // Font length
bool mPrivate; // whether font belongs to a private window
bool mIsBuffer; // whether the font source was a buffer
};
// initially contains a set of userfont font entry objects, replaced with

Просмотреть файл

@ -10,6 +10,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "nsCSSParser.h"
#include "nsCSSRules.h"
#include "nsIDocument.h"
@ -18,8 +19,48 @@
namespace mozilla {
namespace dom {
// -- FontFaceBufferSource ---------------------------------------------------
/**
* An object that wraps a FontFace object and exposes its ArrayBuffer
* or ArrayBufferView data in a form the user font set can consume.
*/
class FontFaceBufferSource : public gfxFontFaceBufferSource
{
public:
FontFaceBufferSource(FontFace* aFontFace)
: mFontFace(aFontFace) {}
virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
private:
nsRefPtr<FontFace> mFontFace;
};
void
FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
{
mFontFace->TakeBuffer(aBuffer, aLength);
}
// -- FontFaceInitializer ----------------------------------------------------
template<typename T>
static void
GetDataFrom(const T& aObject, uint8_t*& aBuffer, uint32_t& aLength)
{
MOZ_ASSERT(!aBuffer);
aObject.ComputeLengthAndData();
// We use moz_malloc here rather than a FallibleTArray or fallible
// operator new[] since the gfxUserFontEntry will be calling moz_free
// on it.
aBuffer = (uint8_t*) moz_malloc(aObject.Length());
if (!aBuffer) {
return;
}
memcpy((void*) aBuffer, aObject.Data(), aObject.Length());
aLength = aObject.Length();
}
/**
* A task that is dispatched to the event queue to call Initialize() on a
* FontFace object with the source information that was passed to the JS
@ -40,6 +81,7 @@ public:
void SetSource(const ArrayBufferView& aArrayBufferView);
NS_IMETHOD Run();
void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
nsRefPtr<FontFace> mFontFace;
FontFace::SourceType mSourceType;
@ -71,14 +113,14 @@ void
FontFaceInitializer::SetSource(const ArrayBuffer& aArrayBuffer)
{
mSourceType = FontFace::eSourceType_Buffer;
// XXX Do something with the array buffer data.
GetDataFrom(aArrayBuffer, mSourceBuffer, mSourceBufferLength);
}
void
FontFaceInitializer::SetSource(const ArrayBufferView& aArrayBufferView)
{
mSourceType = FontFace::eSourceType_Buffer;
// XXX Do something with the array buffer data.
GetDataFrom(aArrayBufferView, mSourceBuffer, mSourceBufferLength);
}
NS_IMETHODIMP
@ -88,6 +130,16 @@ FontFaceInitializer::Run()
return NS_OK;
}
void
FontFaceInitializer::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
{
aBuffer = mSourceBuffer;
aLength = mSourceBufferLength;
mSourceBuffer = nullptr;
mSourceBufferLength = 0;
}
// -- FontFaceStatusSetter ---------------------------------------------------
/**
@ -321,7 +373,23 @@ FontFace::Initialize(FontFaceInitializer* aInitializer)
// We've been given an ArrayBuffer or ArrayBufferView as the source.
MOZ_ASSERT(aInitializer->mSourceType == eSourceType_Buffer);
// XXX Handle array buffers.
mSourceType = aInitializer->mSourceType;
aInitializer->TakeBuffer(mSourceBuffer, mSourceBufferLength);
// Queue a task to set the status to "loading".
nsCOMPtr<nsIRunnable> statusSetterTask =
new FontFaceStatusSetter(this, FontFaceLoadStatus::Loading);
NS_DispatchToMainThread(statusSetterTask);
// We are initialized.
OnInitialized();
// ArrayBuffer(View)-backed FontFace objects are loaded on construction,
// but we need to do this after going through the event loop so that the
// FontFaceStatusSetter runs before us.
nsCOMPtr<nsIRunnable> loaderTask =
NS_NewRunnableMethod(this, &FontFace::DoLoad);
NS_DispatchToMainThread(loaderTask);
}
void
@ -552,8 +620,11 @@ FontFace::SetStatus(FontFaceLoadStatus aStatus)
if (mStatus == FontFaceLoadStatus::Loaded) {
mLoaded->MaybeResolve(this);
} else if (mStatus == FontFaceLoadStatus::Error) {
// XXX Use NS_ERROR_DOM_SYNTAX_ERR for array buffer backed FontFaces.
mLoaded->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
if (mSourceType == eSourceType_Buffer) {
mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
} else {
mLoaded->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
}
}
}
@ -764,6 +835,31 @@ FontFace::DisconnectFromRule()
mInFontFaceSet = false;
}
bool
FontFace::HasFontData() const
{
return mSourceType == eSourceType_Buffer && mSourceBuffer;
}
void
FontFace::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
{
MOZ_ASSERT(HasFontData());
aBuffer = mSourceBuffer;
aLength = mSourceBufferLength;
mSourceBuffer = nullptr;
mSourceBufferLength = 0;
}
already_AddRefed<gfxFontFaceBufferSource>
FontFace::CreateBufferSource()
{
nsRefPtr<FontFaceBufferSource> bufferSource = new FontFaceBufferSource(this);
return bufferSource.forget();
}
// -- FontFace::Entry --------------------------------------------------------
/* virtual */ void

Просмотреть файл

@ -12,12 +12,14 @@
#include "nsCSSValue.h"
#include "nsWrapperCache.h"
class gfxFontFaceBufferSource;
class nsCSSFontFaceRule;
class nsPresContext;
namespace mozilla {
struct CSSFontFaceDescriptors;
namespace dom {
class FontFaceBufferSource;
struct FontFaceDescriptors;
class FontFaceSet;
class FontFaceInitializer;
@ -33,6 +35,7 @@ namespace dom {
class FontFace MOZ_FINAL : public nsISupports,
public nsWrapperCache
{
friend class mozilla::dom::FontFaceBufferSource;
friend class mozilla::dom::FontFaceInitializer;
friend class mozilla::dom::FontFaceStatusSetter;
friend class Entry;
@ -126,6 +129,24 @@ public:
*/
void DisconnectFromRule();
/**
* Returns whether there is an ArrayBuffer or ArrayBufferView of font
* data.
*/
bool HasFontData() const;
/**
* Creates a gfxFontFaceBufferSource to represent the font data
* in this object.
*/
already_AddRefed<gfxFontFaceBufferSource> CreateBufferSource();
/**
* Gets a pointer to and the length of the font data stored in the
* ArrayBuffer or ArrayBufferView.
*/
bool GetData(uint8_t*& aBuffer, uint32_t& aLength);
// Web IDL
static already_AddRefed<FontFace>
Constructor(const GlobalObject& aGlobal,
@ -202,6 +223,11 @@ private:
nsCSSProperty aPropID,
nsString& aResult) const;
/**
* Returns and takes ownership of the buffer storing the font data.
*/
void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
nsCOMPtr<nsISupports> mParent;
nsPresContext* mPresContext;

Просмотреть файл

@ -853,80 +853,89 @@ FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
// set up src array
nsTArray<gfxFontFaceSrc> srcArray;
aFontFace->GetDesc(eCSSFontDesc_Src, val);
unit = val.GetUnit();
if (unit == eCSSUnit_Array) {
nsCSSValue::Array* srcArr = val.GetArrayValue();
size_t numSrc = srcArr->Count();
if (aFontFace->HasFontData()) {
gfxFontFaceSrc* face = srcArray.AppendElement();
if (!face)
return nullptr;
for (size_t i = 0; i < numSrc; i++) {
val = srcArr->Item(i);
unit = val.GetUnit();
gfxFontFaceSrc* face = srcArray.AppendElements(1);
if (!face)
return nullptr;
switch (unit) {
case eCSSUnit_Local_Font:
val.GetStringValue(face->mLocalName);
face->mIsLocal = true;
face->mURI = nullptr;
face->mFormatFlags = 0;
break;
case eCSSUnit_URL:
face->mIsLocal = false;
face->mURI = val.GetURLValue();
face->mReferrer = val.GetURLStructValue()->mReferrer;
face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
// agent and user stylesheets are treated slightly differently,
// the same-site origin check and access control headers are
// enforced against the sheet principal rather than the document
// principal to allow user stylesheets to include @font-face rules
face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
aSheetType == nsStyleSet::eAgentSheet);
face->mLocalName.Truncate();
face->mFormatFlags = 0;
while (i + 1 < numSrc && (val = srcArr->Item(i+1),
val.GetUnit() == eCSSUnit_Font_Format)) {
nsDependentString valueString(val.GetStringBufferValue());
if (valueString.LowerCaseEqualsASCII("woff")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
} else if (valueString.LowerCaseEqualsASCII("opentype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
} else if (valueString.LowerCaseEqualsASCII("truetype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
} else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
} else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
} else if (valueString.LowerCaseEqualsASCII("svg")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
} else {
// unknown format specified, mark to distinguish from the
// case where no format hints are specified
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
}
i++;
}
if (!face->mURI) {
// if URI not valid, omit from src array
srcArray.RemoveElementAt(srcArray.Length() - 1);
NS_WARNING("null url in @font-face rule");
continue;
}
break;
default:
NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
"strange unit type in font-face src array");
break;
}
}
face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
face->mBuffer = aFontFace->CreateBufferSource();
} else {
NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
aFontFace->GetDesc(eCSSFontDesc_Src, val);
unit = val.GetUnit();
if (unit == eCSSUnit_Array) {
nsCSSValue::Array* srcArr = val.GetArrayValue();
size_t numSrc = srcArr->Count();
for (size_t i = 0; i < numSrc; i++) {
val = srcArr->Item(i);
unit = val.GetUnit();
gfxFontFaceSrc* face = srcArray.AppendElements(1);
if (!face)
return nullptr;
switch (unit) {
case eCSSUnit_Local_Font:
val.GetStringValue(face->mLocalName);
face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
face->mURI = nullptr;
face->mFormatFlags = 0;
break;
case eCSSUnit_URL:
face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
face->mURI = val.GetURLValue();
face->mReferrer = val.GetURLStructValue()->mReferrer;
face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
// agent and user stylesheets are treated slightly differently,
// the same-site origin check and access control headers are
// enforced against the sheet principal rather than the document
// principal to allow user stylesheets to include @font-face rules
face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
aSheetType == nsStyleSet::eAgentSheet);
face->mLocalName.Truncate();
face->mFormatFlags = 0;
while (i + 1 < numSrc && (val = srcArr->Item(i+1),
val.GetUnit() == eCSSUnit_Font_Format)) {
nsDependentString valueString(val.GetStringBufferValue());
if (valueString.LowerCaseEqualsASCII("woff")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
} else if (valueString.LowerCaseEqualsASCII("opentype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
} else if (valueString.LowerCaseEqualsASCII("truetype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
} else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
} else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
} else if (valueString.LowerCaseEqualsASCII("svg")) {
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
} else {
// unknown format specified, mark to distinguish from the
// case where no format hints are specified
face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
}
i++;
}
if (!face->mURI) {
// if URI not valid, omit from src array
srcArray.RemoveElementAt(srcArray.Length() - 1);
NS_WARNING("null url in @font-face rule");
continue;
}
break;
default:
NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
"strange unit type in font-face src array");
break;
}
}
} else {
NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
}
}
if (srcArray.IsEmpty()) {
@ -1092,7 +1101,8 @@ FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
if (!ps)
return NS_ERROR_FAILURE;
NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
NS_ASSERTION(aFontFaceSrc &&
aFontFaceSrc->mSourceType == gfxFontFaceSrc::eSourceType_URL,
"bad font face url passed to fontloader");
NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
if (!aFontFaceSrc->mURI)