зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central and inbound
This commit is contained in:
Коммит
271b7ece33
|
@ -1043,9 +1043,8 @@ IDBObjectStore::AppendIndexUpdateInfo(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(val) &&
|
||||
JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) {
|
||||
JS::Rooted<JSObject*> array(aCx, JSVAL_TO_OBJECT(val));
|
||||
if (JS_IsArrayObject(aCx, val)) {
|
||||
JS::Rooted<JSObject*> array(aCx, &val.toObject());
|
||||
uint32_t arrayLength;
|
||||
if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
|
|
|
@ -270,8 +270,7 @@ KeyPath::Parse(JSContext* aCx, const JS::Value& aValue_, KeyPath* aKeyPath)
|
|||
aKeyPath->SetType(NONEXISTENT);
|
||||
|
||||
// See if this is a JS array.
|
||||
if (!JSVAL_IS_PRIMITIVE(aValue) &&
|
||||
JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aValue))) {
|
||||
if (JS_IsArrayObject(aCx, aValue)) {
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(aValue));
|
||||
|
||||
|
|
|
@ -170,8 +170,7 @@ MobileMessageManager::Send(JS::Handle<JS::Value> aNumber,
|
|||
uint8_t aArgc,
|
||||
JS::MutableHandle<JS::Value> aReturn)
|
||||
{
|
||||
if (!aNumber.isString() &&
|
||||
!(aNumber.isObject() && JS_IsArrayObject(aCx, &aNumber.toObject()))) {
|
||||
if (!aNumber.isString() && !JS_IsArrayObject(aCx, aNumber)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
|
@ -340,7 +339,7 @@ MobileMessageManager::Delete(JS::Handle<JS::Value> aParam, JSContext* aCx,
|
|||
|
||||
size = 1;
|
||||
idArray = &id;
|
||||
} else if (!JS_IsArrayObject(aCx, &aParam.toObject())) {
|
||||
} else if (!JS_IsArrayObject(aCx, aParam)) {
|
||||
// Single SmsMessage/MmsMessage object
|
||||
rv = GetMessageId(aCx, aParam, &id);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -37,9 +37,9 @@ class Blob
|
|||
|
||||
public:
|
||||
static JSObject*
|
||||
InitClass(JSContext* aCx, JSObject* aObj)
|
||||
InitClass(JSContext* aCx, JS::Handle<JSObject*> aObj)
|
||||
{
|
||||
return JS_InitClass(aCx, aObj, nullptr, &sClass, Construct, 0,
|
||||
return JS_InitClass(aCx, aObj, JS::NullPtr(), &sClass, Construct, 0,
|
||||
sProperties, sFunctions, nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ class File : public Blob
|
|||
|
||||
public:
|
||||
static JSObject*
|
||||
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
|
||||
InitClass(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<JSObject*> aParentProto)
|
||||
{
|
||||
return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
|
||||
sProperties, nullptr, nullptr, nullptr);
|
||||
|
@ -470,7 +470,7 @@ CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob)
|
|||
bool
|
||||
InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
||||
{
|
||||
JSObject* blobProto = Blob::InitClass(aCx, aGlobal);
|
||||
JS::Rooted<JSObject*> blobProto(aCx, Blob::InitClass(aCx, aGlobal));
|
||||
return blobProto && File::InitClass(aCx, aGlobal, blobProto);
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ GetDirectWriteFaceName(IDWriteFont *aFont,
|
|||
}
|
||||
|
||||
void
|
||||
gfxDWriteFontFamily::FindStyleVariations()
|
||||
gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
|
||||
{
|
||||
HRESULT hr;
|
||||
if (mHasStyles) {
|
||||
|
@ -159,6 +159,9 @@ gfxDWriteFontFamily::FindStyleVariations()
|
|||
|
||||
bool skipFaceNames = mFaceNamesInitialized ||
|
||||
!fp->NeedFullnamePostscriptNames();
|
||||
bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
|
||||
fp->NeedFullnamePostscriptNames() &&
|
||||
aFontInfoData;
|
||||
|
||||
for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
|
||||
nsRefPtr<IDWriteFont> font;
|
||||
|
@ -190,7 +193,15 @@ gfxDWriteFontFamily::FindStyleVariations()
|
|||
|
||||
// postscript/fullname if needed
|
||||
nsAutoString psname, fullname;
|
||||
if (!skipFaceNames) {
|
||||
if (fontInfoShouldHaveFaceNames) {
|
||||
aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
|
||||
if (!fullname.IsEmpty()) {
|
||||
fp->AddFullname(fe, fullname);
|
||||
}
|
||||
if (!psname.IsEmpty()) {
|
||||
fp->AddPostscriptName(fe, psname);
|
||||
}
|
||||
} else if (!skipFaceNames) {
|
||||
hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
|
||||
if (FAILED(hr)) {
|
||||
skipFaceNames = true;
|
||||
|
@ -496,30 +507,38 @@ gfxDWriteFontEntry::GetFontTable(uint32_t aTag)
|
|||
}
|
||||
|
||||
nsresult
|
||||
gfxDWriteFontEntry::ReadCMAP()
|
||||
gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// attempt this once, if errors occur leave a blank cmap
|
||||
if (mCharacterMap) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
nsRefPtr<gfxCharacterMap> charmap;
|
||||
nsresult rv;
|
||||
bool symbolFont;
|
||||
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
AutoTable cmapTable(this, kCMAP);
|
||||
if (cmapTable) {
|
||||
bool unicodeFont = false, symbolFont = false; // currently ignored
|
||||
uint32_t cmapLen;
|
||||
const uint8_t* cmapData =
|
||||
reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
|
||||
&cmapLen));
|
||||
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
|
||||
mUVSOffset,
|
||||
symbolFont))) {
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
charmap = new gfxCharacterMap();
|
||||
AutoTable cmapTable(this, kCMAP);
|
||||
|
||||
if (cmapTable) {
|
||||
bool unicodeFont = false, symbolFont = false; // currently ignored
|
||||
uint32_t cmapLen;
|
||||
const uint8_t* cmapData =
|
||||
reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
|
||||
&cmapLen));
|
||||
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
} else {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
mHasCmapTable = NS_SUCCEEDED(rv);
|
||||
|
@ -1534,3 +1553,189 @@ gfxDWriteFontList::GlobalFontFallback(const uint32_t aCh,
|
|||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// used to load system-wide font info on off-main thread
|
||||
class DirectWriteFontInfo : public FontInfoData {
|
||||
public:
|
||||
DirectWriteFontInfo(bool aLoadOtherNames,
|
||||
bool aLoadFaceNames,
|
||||
bool aLoadCmaps) :
|
||||
FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
|
||||
{}
|
||||
|
||||
virtual ~DirectWriteFontInfo() {}
|
||||
|
||||
// loads font data for all members of a given family
|
||||
virtual void LoadFontFamilyData(const nsAString& aFamilyName);
|
||||
|
||||
nsRefPtr<IDWriteFontCollection> mSystemFonts;
|
||||
};
|
||||
|
||||
void
|
||||
DirectWriteFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
|
||||
{
|
||||
// lookup the family
|
||||
nsAutoTArray<char16_t, 32> famName;
|
||||
|
||||
uint32_t len = aFamilyName.Length();
|
||||
famName.SetLength(len + 1);
|
||||
memcpy(famName.Elements(), aFamilyName.BeginReading(), len * sizeof(char16_t));
|
||||
famName[len] = 0;
|
||||
|
||||
HRESULT hr;
|
||||
BOOL exists = false;
|
||||
|
||||
uint32_t index;
|
||||
hr = mSystemFonts->FindFamilyName(famName.Elements(), &index, &exists);
|
||||
if (FAILED(hr) || !exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<IDWriteFontFamily> family;
|
||||
mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
|
||||
if (!family) {
|
||||
return;
|
||||
}
|
||||
|
||||
// later versions of DirectWrite support querying the fullname/psname
|
||||
bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
|
||||
|
||||
for (uint32_t i = 0; i < family->GetFontCount(); i++) {
|
||||
// get the font
|
||||
nsRefPtr<IDWriteFont> dwFont;
|
||||
hr = family->GetFont(i, getter_AddRefs(dwFont));
|
||||
if (FAILED(hr)) {
|
||||
// This should never happen.
|
||||
NS_WARNING("Failed to get existing font from family.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dwFont->GetSimulations() & DWRITE_FONT_SIMULATIONS_OBLIQUE) {
|
||||
// We don't want these.
|
||||
continue;
|
||||
}
|
||||
|
||||
mLoadStats.fonts++;
|
||||
|
||||
// get the name of the face
|
||||
nsString fullID(aFamilyName);
|
||||
nsAutoString fontName;
|
||||
hr = GetDirectWriteFontName(dwFont, fontName);
|
||||
if (FAILED(hr)) {
|
||||
continue;
|
||||
}
|
||||
fullID.Append(NS_LITERAL_STRING(" "));
|
||||
fullID.Append(fontName);
|
||||
|
||||
FontFaceData fontData;
|
||||
bool haveData = true;
|
||||
nsRefPtr<IDWriteFontFace> dwFontFace;
|
||||
|
||||
if (mLoadFaceNames) {
|
||||
// try to load using DirectWrite first
|
||||
if (loadFaceNamesUsingDirectWrite) {
|
||||
hr = GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
|
||||
if (FAILED(hr)) {
|
||||
loadFaceNamesUsingDirectWrite = false;
|
||||
}
|
||||
hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
|
||||
if (FAILED(hr)) {
|
||||
loadFaceNamesUsingDirectWrite = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if DirectWrite read fails, load directly from name table
|
||||
if (!loadFaceNamesUsingDirectWrite) {
|
||||
hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
|
||||
if (SUCCEEDED(hr)) {
|
||||
uint32_t kNAME =
|
||||
NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
|
||||
const char *nameData;
|
||||
BOOL exists;
|
||||
void* ctx;
|
||||
uint32_t nameSize;
|
||||
|
||||
hr = dwFontFace->TryGetFontTable(
|
||||
kNAME,
|
||||
(const void**)&nameData, &nameSize, &ctx, &exists);
|
||||
|
||||
if (SUCCEEDED(hr) && nameData && nameSize > 0) {
|
||||
gfxFontUtils::ReadCanonicalName(nameData, nameSize,
|
||||
gfxFontUtils::NAME_ID_FULL,
|
||||
fontData.mFullName);
|
||||
gfxFontUtils::ReadCanonicalName(nameData, nameSize,
|
||||
gfxFontUtils::NAME_ID_POSTSCRIPT,
|
||||
fontData.mPostscriptName);
|
||||
dwFontFace->ReleaseFontTable(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
haveData = !fontData.mPostscriptName.IsEmpty() ||
|
||||
!fontData.mFullName.IsEmpty();
|
||||
if (haveData) {
|
||||
mLoadStats.facenames++;
|
||||
}
|
||||
}
|
||||
|
||||
// cmaps
|
||||
if (mLoadCmaps) {
|
||||
if (!dwFontFace) {
|
||||
hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
|
||||
if (!SUCCEEDED(hr)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t kCMAP =
|
||||
NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
|
||||
const uint8_t *cmapData;
|
||||
BOOL exists;
|
||||
void* ctx;
|
||||
uint32_t cmapSize;
|
||||
|
||||
hr = dwFontFace->TryGetFontTable(kCMAP,
|
||||
(const void**)&cmapData, &cmapSize, &ctx, &exists);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
bool cmapLoaded = false;
|
||||
bool unicodeFont = false, symbolFont = false;
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
uint32_t offset;
|
||||
|
||||
if (cmapData &&
|
||||
cmapSize > 0 &&
|
||||
NS_SUCCEEDED(
|
||||
gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
|
||||
offset, unicodeFont, symbolFont))) {
|
||||
fontData.mCharacterMap = charmap;
|
||||
fontData.mUVSOffset = offset;
|
||||
fontData.mSymbolFont = symbolFont;
|
||||
cmapLoaded = true;
|
||||
mLoadStats.cmaps++;
|
||||
}
|
||||
dwFontFace->ReleaseFontTable(ctx);
|
||||
haveData = haveData || cmapLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
// if have data, load
|
||||
if (haveData) {
|
||||
mFontFaceData.Put(fullID, fontData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<FontInfoData>
|
||||
gfxDWriteFontList::CreateFontInfoData()
|
||||
{
|
||||
bool loadCmaps = !UsesSystemFallback() ||
|
||||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
|
||||
|
||||
nsRefPtr<DirectWriteFontInfo> fi =
|
||||
new DirectWriteFontInfo(false, NeedFullnamePostscriptNames(), loadCmaps);
|
||||
gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
|
||||
GetSystemFontCollection(getter_AddRefs(fi->mSystemFonts));
|
||||
|
||||
return fi.forget();
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
: gfxFontFamily(aName), mDWFamily(aFamily), mForceGDIClassic(false) {}
|
||||
virtual ~gfxDWriteFontFamily();
|
||||
|
||||
virtual void FindStyleVariations();
|
||||
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
virtual void LocalizedName(nsAString& aLocalizedName);
|
||||
|
||||
|
@ -149,7 +149,7 @@ public:
|
|||
|
||||
virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE;
|
||||
|
||||
nsresult ReadCMAP();
|
||||
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
bool IsCJKFont();
|
||||
|
||||
|
@ -407,6 +407,8 @@ private:
|
|||
bool mInitialized;
|
||||
virtual nsresult DelayedInitFontList();
|
||||
|
||||
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
|
||||
|
||||
gfxFloat mForceGDIClassicMaxFontSize;
|
||||
|
||||
// whether to use GDI font table access routines
|
||||
|
|
|
@ -463,7 +463,7 @@ FT2FontEntry::CairoFontFace()
|
|||
}
|
||||
|
||||
nsresult
|
||||
FT2FontEntry::ReadCMAP()
|
||||
FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
||||
{
|
||||
if (mCharacterMap) {
|
||||
return NS_OK;
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
// This may fail and return null, so caller must be prepared to handle this.
|
||||
cairo_scaled_font_t *CreateScaledFont(const gfxFontStyle *aStyle);
|
||||
|
||||
nsresult ReadCMAP();
|
||||
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
|
|||
return 0;
|
||||
}
|
||||
|
||||
nsresult gfxFontEntry::ReadCMAP()
|
||||
nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
||||
{
|
||||
NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
|
||||
mCharacterMap = new gfxCharacterMap();
|
||||
|
@ -564,6 +564,18 @@ gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxCharacterMap>
|
||||
gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
|
||||
uint32_t& aUVSOffset,
|
||||
bool& aSymbolFont)
|
||||
{
|
||||
if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
|
||||
}
|
||||
|
||||
hb_blob_t *
|
||||
gfxFontEntry::GetFontTable(uint32_t aTag)
|
||||
{
|
||||
|
@ -905,6 +917,11 @@ gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
|
|||
void
|
||||
gfxFontFamily::CheckForSimpleFamily()
|
||||
{
|
||||
// already checked this family
|
||||
if (mIsSimpleFamily) {
|
||||
return;
|
||||
};
|
||||
|
||||
uint32_t count = mAvailableFonts.Length();
|
||||
if (count > 4 || count == 0) {
|
||||
return; // can't be "simple" if there are >4 faces;
|
||||
|
@ -1147,6 +1164,54 @@ gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
|
|||
}
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
|
||||
const char *aNameData,
|
||||
uint32_t aDataLength,
|
||||
nsTArray<nsString>& aOtherFamilyNames,
|
||||
bool useFullName)
|
||||
{
|
||||
const gfxFontUtils::NameHeader *nameHeader =
|
||||
reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
|
||||
|
||||
uint32_t nameCount = nameHeader->count;
|
||||
if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
|
||||
NS_WARNING("invalid font (name records)");
|
||||
return;
|
||||
}
|
||||
|
||||
const gfxFontUtils::NameRecord *nameRecord =
|
||||
reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
|
||||
uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
|
||||
|
||||
for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
|
||||
uint32_t nameLen = nameRecord->length;
|
||||
uint32_t nameOff = nameRecord->offset; // offset from base of string storage
|
||||
|
||||
if (stringsBase + nameOff + nameLen > aDataLength) {
|
||||
NS_WARNING("invalid font (name table strings)");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t nameID = nameRecord->nameID;
|
||||
if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
|
||||
(!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
|
||||
nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
|
||||
nsAutoString otherFamilyName;
|
||||
bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
|
||||
nameLen,
|
||||
uint32_t(nameRecord->platformID),
|
||||
uint32_t(nameRecord->encodingID),
|
||||
uint32_t(nameRecord->languageID),
|
||||
otherFamilyName);
|
||||
// add if not same as canonical family name
|
||||
if (ok && otherFamilyName != aFamilyName) {
|
||||
aOtherFamilyNames.AppendElement(otherFamilyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if other names were found, false otherwise
|
||||
bool
|
||||
gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
|
||||
|
@ -1155,52 +1220,19 @@ gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontLis
|
|||
{
|
||||
uint32_t dataLength;
|
||||
const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
|
||||
const gfxFontUtils::NameHeader *nameHeader =
|
||||
reinterpret_cast<const gfxFontUtils::NameHeader*>(nameData);
|
||||
nsAutoTArray<nsString,4> otherFamilyNames;
|
||||
|
||||
uint32_t nameCount = nameHeader->count;
|
||||
if (nameCount * sizeof(gfxFontUtils::NameRecord) > dataLength) {
|
||||
NS_WARNING("invalid font (name records)");
|
||||
return false;
|
||||
}
|
||||
|
||||
const gfxFontUtils::NameRecord *nameRecord =
|
||||
reinterpret_cast<const gfxFontUtils::NameRecord*>(nameData + sizeof(gfxFontUtils::NameHeader));
|
||||
uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
|
||||
ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
|
||||
otherFamilyNames, useFullName);
|
||||
|
||||
bool foundNames = false;
|
||||
for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
|
||||
uint32_t nameLen = nameRecord->length;
|
||||
uint32_t nameOff = nameRecord->offset; // offset from base of string storage
|
||||
|
||||
if (stringsBase + nameOff + nameLen > dataLength) {
|
||||
NS_WARNING("invalid font (name table strings)");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t nameID = nameRecord->nameID;
|
||||
if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
|
||||
(!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
|
||||
nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
|
||||
nsAutoString otherFamilyName;
|
||||
bool ok = gfxFontUtils::DecodeFontName(nameData + stringsBase + nameOff,
|
||||
nameLen,
|
||||
uint32_t(nameRecord->platformID),
|
||||
uint32_t(nameRecord->encodingID),
|
||||
uint32_t(nameRecord->languageID),
|
||||
otherFamilyName);
|
||||
// add if not same as canonical family name
|
||||
if (ok && otherFamilyName != mName) {
|
||||
aPlatformFontList->AddOtherFamilyName(this, otherFamilyName);
|
||||
foundNames = true;
|
||||
}
|
||||
}
|
||||
uint32_t n = otherFamilyNames.Length();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
|
||||
}
|
||||
|
||||
return foundNames;
|
||||
return n != 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
|
||||
{
|
||||
|
@ -1251,18 +1283,46 @@ gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
|
|||
|
||||
void
|
||||
gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
|
||||
bool aNeedFullnamePostscriptNames)
|
||||
bool aNeedFullnamePostscriptNames,
|
||||
FontInfoData *aFontInfoData)
|
||||
{
|
||||
// if all needed names have already been read, skip
|
||||
if (mOtherFamilyNamesInitialized &&
|
||||
(mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
|
||||
return;
|
||||
|
||||
FindStyleVariations();
|
||||
if (!mOtherFamilyNamesInitialized &&
|
||||
aFontInfoData &&
|
||||
aFontInfoData->mLoadOtherNames)
|
||||
{
|
||||
nsAutoTArray<nsString,4> otherFamilyNames;
|
||||
bool foundOtherNames =
|
||||
aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
|
||||
if (foundOtherNames) {
|
||||
uint32_t i, n = otherFamilyNames.Length();
|
||||
for (i = 0; i < n; i++) {
|
||||
aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
|
||||
}
|
||||
}
|
||||
mOtherFamilyNamesInitialized = true;
|
||||
}
|
||||
|
||||
// if all needed data has been initialized, return
|
||||
if (mOtherFamilyNamesInitialized &&
|
||||
(mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FindStyleVariations(aFontInfoData);
|
||||
|
||||
// check again, as style enumeration code may have loaded names
|
||||
if (mOtherFamilyNamesInitialized &&
|
||||
(mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t i, numFonts = mAvailableFonts.Length();
|
||||
const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
|
||||
nsAutoString fullname, psname;
|
||||
|
||||
bool firstTime = true, readAllFaces = false;
|
||||
for (i = 0; i < numFonts; ++i) {
|
||||
|
@ -1270,11 +1330,35 @@ gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
|
|||
if (!fe) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString fullname, psname;
|
||||
bool foundFaceNames = false;
|
||||
if (!mFaceNamesInitialized &&
|
||||
aNeedFullnamePostscriptNames &&
|
||||
aFontInfoData &&
|
||||
aFontInfoData->mLoadFaceNames) {
|
||||
aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
|
||||
if (!fullname.IsEmpty()) {
|
||||
aPlatformFontList->AddFullname(fe, fullname);
|
||||
}
|
||||
if (!psname.IsEmpty()) {
|
||||
aPlatformFontList->AddPostscriptName(fe, psname);
|
||||
}
|
||||
foundFaceNames = true;
|
||||
|
||||
// found everything needed? skip to next font
|
||||
if (mOtherFamilyNamesInitialized) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// load directly from the name table
|
||||
gfxFontEntry::AutoTable nameTable(fe, kNAME);
|
||||
if (!nameTable) {
|
||||
continue;
|
||||
}
|
||||
if (aNeedFullnamePostscriptNames) {
|
||||
|
||||
if (aNeedFullnamePostscriptNames && !foundFaceNames) {
|
||||
if (gfxFontUtils::ReadCanonicalName(
|
||||
nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
|
||||
{
|
||||
|
@ -1325,6 +1409,25 @@ gfxFontFamily::FindFont(const nsAString& aPostscriptName)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
|
||||
{
|
||||
FindStyleVariations(aFontInfoData);
|
||||
|
||||
uint32_t i, numFonts = mAvailableFonts.Length();
|
||||
for (i = 0; i < numFonts; i++) {
|
||||
gfxFontEntry *fe = mAvailableFonts[i];
|
||||
// don't try to load cmaps for downloadable fonts not yet loaded
|
||||
if (!fe || fe->mIsProxy) {
|
||||
continue;
|
||||
}
|
||||
fe->ReadCMAP(aFontInfoData);
|
||||
mFamilyCharacterMap.Union(*(fe->mCharacterMap));
|
||||
}
|
||||
mFamilyCharacterMap.Compact();
|
||||
mFamilyCharacterMapInitialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
|
||||
FontListSizes* aSizes) const
|
||||
|
|
|
@ -49,6 +49,7 @@ class gfxShapedText;
|
|||
class gfxShapedWord;
|
||||
class gfxSVGGlyphs;
|
||||
class gfxTextContextPaint;
|
||||
class FontInfoData;
|
||||
|
||||
class nsILanguageAtomService;
|
||||
|
||||
|
@ -299,7 +300,7 @@ public:
|
|||
// ReadCMAP() must *always* set the mCharacterMap pointer to a valid
|
||||
// gfxCharacterMap, even if empty, as other code assumes this pointer
|
||||
// can be safely dereferenced.
|
||||
virtual nsresult ReadCMAP();
|
||||
virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
bool TryGetSVGData(gfxFont* aFont);
|
||||
bool HasSVGGlyph(uint32_t aGlyphId);
|
||||
|
@ -491,6 +492,12 @@ protected:
|
|||
// caller is responsible to do any sanitization/validation necessary.
|
||||
hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
|
||||
|
||||
// lookup the cmap in cached font data
|
||||
virtual already_AddRefed<gfxCharacterMap>
|
||||
GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
|
||||
uint32_t& aUVSOffset,
|
||||
bool& aSymbolFont);
|
||||
|
||||
// Font's unitsPerEm from the 'head' table, if available (will be set to
|
||||
// kInvalidUPEM for non-sfnt font formats)
|
||||
uint16_t mUnitsPerEm;
|
||||
|
@ -693,6 +700,7 @@ public:
|
|||
}
|
||||
|
||||
// note that the styles for this family have been added
|
||||
bool HasStyles() { return mHasStyles; }
|
||||
void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
|
||||
|
||||
// choose a specific face to match a style using CSS font matching
|
||||
|
@ -713,6 +721,14 @@ public:
|
|||
// read in other family names, if any, and use functor to add each into cache
|
||||
virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
|
||||
|
||||
// helper method for reading localized family names from the name table
|
||||
// of a single face
|
||||
static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
|
||||
const char *aNameData,
|
||||
uint32_t aDataLength,
|
||||
nsTArray<nsString>& aOtherFamilyNames,
|
||||
bool useFullName);
|
||||
|
||||
// set when other family names have been read in
|
||||
void SetOtherFamilyNamesInitialized() {
|
||||
mOtherFamilyNamesInitialized = true;
|
||||
|
@ -721,30 +737,18 @@ public:
|
|||
// read in other localized family names, fullnames and Postscript names
|
||||
// for all faces and append to lookup tables
|
||||
virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
|
||||
bool aNeedFullnamePostscriptNames);
|
||||
bool aNeedFullnamePostscriptNames,
|
||||
FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
// find faces belonging to this family (platform implementations override this;
|
||||
// should be made pure virtual once all subclasses have been updated)
|
||||
virtual void FindStyleVariations() { }
|
||||
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { }
|
||||
|
||||
// search for a specific face using the Postscript name
|
||||
gfxFontEntry* FindFont(const nsAString& aPostscriptName);
|
||||
|
||||
// read in cmaps for all the faces
|
||||
void ReadAllCMAPs() {
|
||||
uint32_t i, numFonts = mAvailableFonts.Length();
|
||||
for (i = 0; i < numFonts; i++) {
|
||||
gfxFontEntry *fe = mAvailableFonts[i];
|
||||
// don't try to load cmaps for downloadable fonts not yet loaded
|
||||
if (!fe || fe->mIsProxy) {
|
||||
continue;
|
||||
}
|
||||
fe->ReadCMAP();
|
||||
mFamilyCharacterMap.Union(*(fe->mCharacterMap));
|
||||
}
|
||||
mFamilyCharacterMap.Compact();
|
||||
mFamilyCharacterMapInitialized = true;
|
||||
}
|
||||
void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
bool TestCharacterMap(uint32_t aCh) {
|
||||
if (!mFamilyCharacterMapInitialized) {
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "gfxFontInfoLoader.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h" // for nsRunnable
|
||||
#include "gfxPlatformFontList.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::services::GetObserverService;
|
||||
|
||||
void
|
||||
FontInfoData::Load()
|
||||
{
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
|
||||
uint32_t i, n = mFontFamiliesToLoad.Length();
|
||||
mLoadStats.families = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
LoadFontFamilyData(mFontFamiliesToLoad[i]);
|
||||
}
|
||||
|
||||
mLoadTime = TimeStamp::Now() - start;
|
||||
}
|
||||
|
||||
class FontInfoLoadCompleteEvent : public nsRunnable {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) :
|
||||
mFontInfo(aFontInfo)
|
||||
{}
|
||||
virtual ~FontInfoLoadCompleteEvent() {}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsRefPtr<FontInfoData> mFontInfo;
|
||||
};
|
||||
|
||||
class AsyncFontInfoLoader : public nsRunnable {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
AsyncFontInfoLoader(FontInfoData *aFontInfo) :
|
||||
mFontInfo(aFontInfo)
|
||||
{
|
||||
mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
|
||||
}
|
||||
virtual ~AsyncFontInfoLoader() {}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsRefPtr<FontInfoData> mFontInfo;
|
||||
nsRefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
|
||||
};
|
||||
|
||||
// runs on main thread after async font info loading is done
|
||||
nsresult
|
||||
FontInfoLoadCompleteEvent::Run()
|
||||
{
|
||||
gfxFontInfoLoader *loader =
|
||||
static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
|
||||
|
||||
loader->FinalizeLoader(mFontInfo);
|
||||
|
||||
mFontInfo = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(FontInfoLoadCompleteEvent, nsIRunnable);
|
||||
|
||||
// runs on separate thread
|
||||
nsresult
|
||||
AsyncFontInfoLoader::Run()
|
||||
{
|
||||
// load platform-specific font info
|
||||
mFontInfo->Load();
|
||||
|
||||
// post a completion event that transfer the data to the fontlist
|
||||
NS_DispatchToMainThread(mCompleteEvent, NS_DISPATCH_NORMAL);
|
||||
mFontInfo = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(AsyncFontInfoLoader, nsIRunnable);
|
||||
|
||||
NS_IMPL_ISUPPORTS1(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *someData)
|
||||
{
|
||||
if (!nsCRT::strcmp(aTopic, "quit-application")) {
|
||||
mLoader->CancelLoader();
|
||||
} else {
|
||||
NS_NOTREACHED("unexpected notification topic");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
|
||||
{
|
||||
mInterval = aInterval;
|
||||
|
||||
// sanity check
|
||||
if (mState != stateInitial &&
|
||||
mState != stateTimerOff &&
|
||||
mState != stateTimerOnDelay) {
|
||||
CancelLoader();
|
||||
}
|
||||
|
||||
// set up timer
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!mTimer) {
|
||||
NS_WARNING("Failure to create font info loader timer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AddShutdownObserver();
|
||||
|
||||
// delay? ==> start async thread after a delay
|
||||
if (aDelay) {
|
||||
mState = stateTimerOnDelay;
|
||||
mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
return;
|
||||
}
|
||||
|
||||
mFontInfo = CreateFontInfoData();
|
||||
|
||||
// initialize
|
||||
InitLoader();
|
||||
|
||||
// start async load
|
||||
mState = stateAsyncLoad;
|
||||
nsresult rv = NS_NewNamedThread("Font Loader",
|
||||
getter_AddRefs(mFontLoaderThread),
|
||||
nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
|
||||
|
||||
mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo)
|
||||
{
|
||||
// avoid loading data if loader has already been canceled
|
||||
if (mState != stateAsyncLoad) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLoadTime = mFontInfo->mLoadTime;
|
||||
|
||||
// try to load all font data immediately
|
||||
if (LoadFontInfo()) {
|
||||
CancelLoader();
|
||||
return;
|
||||
}
|
||||
|
||||
// not all work completed ==> run load on interval
|
||||
mState = stateTimerOnInterval;
|
||||
mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::CancelLoader()
|
||||
{
|
||||
if (mState == stateInitial) {
|
||||
return;
|
||||
}
|
||||
mState = stateTimerOff;
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
if (mFontLoaderThread) {
|
||||
mFontLoaderThread->Shutdown();
|
||||
mFontLoaderThread = nullptr;
|
||||
}
|
||||
RemoveShutdownObserver();
|
||||
CleanupLoader();
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::LoadFontInfoTimerFire()
|
||||
{
|
||||
if (mState == stateTimerOnDelay) {
|
||||
mState = stateTimerOnInterval;
|
||||
mTimer->SetDelay(mInterval);
|
||||
}
|
||||
|
||||
bool done = LoadFontInfo();
|
||||
if (done) {
|
||||
CancelLoader();
|
||||
}
|
||||
}
|
||||
|
||||
gfxFontInfoLoader::~gfxFontInfoLoader()
|
||||
{
|
||||
RemoveShutdownObserver();
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::AddShutdownObserver()
|
||||
{
|
||||
if (mObserver) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
if (obs) {
|
||||
mObserver = new ShutdownObserver(this);
|
||||
obs->AddObserver(mObserver, "quit-application", false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::RemoveShutdownObserver()
|
||||
{
|
||||
if (mObserver) {
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(mObserver, "quit-application");
|
||||
mObserver = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
#ifndef GFX_FONT_INFO_LOADER_H
|
||||
#define GFX_FONT_INFO_LOADER_H
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "gfxFont.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
|
||||
// data retrieved for a given face
|
||||
|
||||
struct FontFaceData {
|
||||
FontFaceData() : mUVSOffset(0), mSymbolFont(false) {}
|
||||
|
||||
FontFaceData(const FontFaceData& aFontFaceData) {
|
||||
mFullName = aFontFaceData.mFullName;
|
||||
mPostscriptName = aFontFaceData.mPostscriptName;
|
||||
mCharacterMap = aFontFaceData.mCharacterMap;
|
||||
mUVSOffset = aFontFaceData.mUVSOffset;
|
||||
mSymbolFont = aFontFaceData.mSymbolFont;
|
||||
}
|
||||
|
||||
nsString mFullName;
|
||||
nsString mPostscriptName;
|
||||
nsRefPtr<gfxCharacterMap> mCharacterMap;
|
||||
uint32_t mUVSOffset;
|
||||
bool mSymbolFont;
|
||||
};
|
||||
|
||||
// base class used to contain cached system-wide font info.
|
||||
// methods in this class are called on off-main threads so
|
||||
// all methods use only static methods or other thread-safe
|
||||
// font data access API's. specifically, no use is made of
|
||||
// gfxPlatformFontList, gfxFontFamily, gfxFamily or any
|
||||
// harfbuzz API methods within FontInfoData subclasses.
|
||||
|
||||
class FontInfoData {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontInfoData)
|
||||
|
||||
FontInfoData(bool aLoadOtherNames,
|
||||
bool aLoadFaceNames,
|
||||
bool aLoadCmaps) :
|
||||
mLoadOtherNames(aLoadOtherNames),
|
||||
mLoadFaceNames(aLoadFaceNames),
|
||||
mLoadCmaps(aLoadCmaps)
|
||||
{
|
||||
MOZ_COUNT_CTOR(FontInfoData);
|
||||
}
|
||||
|
||||
virtual ~FontInfoData() {
|
||||
MOZ_COUNT_DTOR(FontInfoData);
|
||||
}
|
||||
|
||||
virtual void Load();
|
||||
|
||||
// loads font data for all fonts of a given family
|
||||
// (called on async thread)
|
||||
virtual void LoadFontFamilyData(const nsAString& aFamilyName) = 0;
|
||||
|
||||
// -- methods overriden by platform-specific versions --
|
||||
|
||||
// fetches cmap data for a particular font from cached font data
|
||||
virtual already_AddRefed<gfxCharacterMap>
|
||||
GetCMAP(const nsAString& aFontName,
|
||||
uint32_t& aUVSOffset,
|
||||
bool& aSymbolFont)
|
||||
{
|
||||
FontFaceData faceData;
|
||||
if (!mFontFaceData.Get(aFontName, &faceData) ||
|
||||
!faceData.mCharacterMap) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aUVSOffset = faceData.mUVSOffset;
|
||||
aSymbolFont = faceData.mSymbolFont;
|
||||
nsRefPtr<gfxCharacterMap> cmap = faceData.mCharacterMap;
|
||||
return cmap.forget();
|
||||
}
|
||||
|
||||
// fetches fullname/postscript names from cached font data
|
||||
virtual void GetFaceNames(const nsAString& aFontName,
|
||||
nsAString& aFullName,
|
||||
nsAString& aPostscriptName)
|
||||
{
|
||||
FontFaceData faceData;
|
||||
if (!mFontFaceData.Get(aFontName, &faceData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aFullName = faceData.mFullName;
|
||||
aPostscriptName = faceData.mPostscriptName;
|
||||
}
|
||||
|
||||
// fetches localized family name data from cached font data
|
||||
virtual bool GetOtherFamilyNames(const nsAString& aFamilyName,
|
||||
nsTArray<nsString>& aOtherFamilyNames)
|
||||
{
|
||||
return mOtherFamilyNames.Get(aFamilyName, &aOtherFamilyNames);
|
||||
}
|
||||
|
||||
nsTArray<nsString> mFontFamiliesToLoad;
|
||||
|
||||
// time spent on the loader thread
|
||||
mozilla::TimeDuration mLoadTime;
|
||||
|
||||
struct FontCounts {
|
||||
uint32_t families;
|
||||
uint32_t fonts;
|
||||
uint32_t cmaps;
|
||||
uint32_t facenames;
|
||||
uint32_t othernames;
|
||||
};
|
||||
|
||||
FontCounts mLoadStats;
|
||||
|
||||
bool mLoadOtherNames;
|
||||
bool mLoadFaceNames;
|
||||
bool mLoadCmaps;
|
||||
|
||||
// face name ==> per-face data
|
||||
nsDataHashtable<nsStringHashKey, FontFaceData> mFontFaceData;
|
||||
|
||||
// canonical family name ==> array of localized family names
|
||||
nsDataHashtable<nsStringHashKey, nsTArray<nsString> > mOtherFamilyNames;
|
||||
};
|
||||
|
||||
// gfxFontInfoLoader - helper class for loading font info on async thread
|
||||
// For large, "all fonts on system" data, data needed on a given platform
|
||||
// (e.g. localized names, face names, cmaps) are loaded async.
|
||||
|
||||
// helper class for loading in font info on a separate async thread
|
||||
// once async thread completes, completion process is run on regular
|
||||
// intervals to prevent tying up the main thread
|
||||
|
||||
class gfxFontInfoLoader {
|
||||
public:
|
||||
|
||||
// state transitions:
|
||||
// initial ---StartLoader with delay---> timer on delay
|
||||
// initial ---StartLoader without delay---> timer on interval
|
||||
// timer on delay ---LoaderTimerFire---> timer on interval
|
||||
// timer on delay ---CancelLoader---> timer off
|
||||
// timer on interval ---CancelLoader---> timer off
|
||||
// timer off ---StartLoader with delay---> timer on delay
|
||||
// timer off ---StartLoader without delay---> timer on interval
|
||||
typedef enum {
|
||||
stateInitial,
|
||||
stateTimerOnDelay,
|
||||
stateAsyncLoad,
|
||||
stateTimerOnInterval,
|
||||
stateTimerOff
|
||||
} TimerState;
|
||||
|
||||
gfxFontInfoLoader() :
|
||||
mInterval(0), mState(stateInitial)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~gfxFontInfoLoader();
|
||||
|
||||
// start timer with an initial delay, then call Run method at regular intervals
|
||||
void StartLoader(uint32_t aDelay, uint32_t aInterval);
|
||||
|
||||
// Finalize - async load complete, transfer data (on intervals if necessary)
|
||||
virtual void FinalizeLoader(FontInfoData *aFontInfo);
|
||||
|
||||
// cancel the timer and cleanup
|
||||
void CancelLoader();
|
||||
|
||||
uint32_t GetInterval() { return mInterval; }
|
||||
|
||||
protected:
|
||||
class ShutdownObserver : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
ShutdownObserver(gfxFontInfoLoader *aLoader)
|
||||
: mLoader(aLoader)
|
||||
{ }
|
||||
|
||||
virtual ~ShutdownObserver()
|
||||
{ }
|
||||
|
||||
protected:
|
||||
gfxFontInfoLoader *mLoader;
|
||||
};
|
||||
|
||||
// CreateFontInfo - create platform-specific object used
|
||||
// to load system-wide font info
|
||||
virtual already_AddRefed<FontInfoData> CreateFontInfoData() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Init - initialization before async loader thread runs
|
||||
virtual void InitLoader() = 0;
|
||||
|
||||
// LoadFontInfo - transfer font info data within a time limit, return
|
||||
// true when done
|
||||
virtual bool LoadFontInfo() = 0;
|
||||
|
||||
// Cleanup - finish and cleanup after done, including possible reflows
|
||||
virtual void CleanupLoader() {
|
||||
mFontInfo = nullptr;
|
||||
}
|
||||
|
||||
// Timer interval callbacks
|
||||
static void LoadFontInfoCallback(nsITimer *aTimer, void *aThis) {
|
||||
gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
|
||||
loader->LoadFontInfoTimerFire();
|
||||
}
|
||||
|
||||
static void DelayedStartCallback(nsITimer *aTimer, void *aThis) {
|
||||
gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
|
||||
loader->StartLoader(0, loader->GetInterval());
|
||||
}
|
||||
|
||||
void LoadFontInfoTimerFire();
|
||||
|
||||
void AddShutdownObserver();
|
||||
void RemoveShutdownObserver();
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsIObserver> mObserver;
|
||||
nsCOMPtr<nsIThread> mFontLoaderThread;
|
||||
uint32_t mInterval;
|
||||
TimerState mState;
|
||||
|
||||
// after async font loader completes, data is stored here
|
||||
nsRefPtr<FontInfoData> mFontInfo;
|
||||
|
||||
// time spent on the loader thread
|
||||
mozilla::TimeDuration mLoadTime;
|
||||
};
|
||||
|
||||
#endif /* GFX_FONT_INFO_LOADER_H */
|
|
@ -18,10 +18,9 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
#include "nsCRT.h"
|
||||
|
||||
#include "harfbuzz/hb.h"
|
||||
|
||||
|
@ -38,7 +37,6 @@
|
|||
#define UNICODE_BMP_LIMIT 0x10000
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::services::GetObserverService;
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
|
@ -1087,40 +1085,53 @@ enum {
|
|||
};
|
||||
|
||||
nsresult
|
||||
gfxFontUtils::ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
int32_t aPlatformID, nsTArray<nsString>& aNames)
|
||||
gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
|
||||
uint32_t aNameID, int32_t aPlatformID,
|
||||
nsTArray<nsString>& aNames)
|
||||
{
|
||||
return ReadNames(aNameTable, aNameID, LANG_ALL, aPlatformID, aNames);
|
||||
return ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
|
||||
aPlatformID, aNames);
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxFontUtils::ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
gfxFontUtils::ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
nsString& aName)
|
||||
{
|
||||
uint32_t nameTableLen;
|
||||
const char *nameTable = hb_blob_get_data(aNameTable, &nameTableLen);
|
||||
return ReadCanonicalName(nameTable, nameTableLen, aNameID, aName);
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxFontUtils::ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
|
||||
uint32_t aNameID, nsString& aName)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsTArray<nsString> names;
|
||||
|
||||
// first, look for the English name (this will succeed 99% of the time)
|
||||
rv = ReadNames(aNameTable, aNameID, CANONICAL_LANG_ID, PLATFORM_ID, names);
|
||||
rv = ReadNames(aNameData, aDataLen, aNameID, CANONICAL_LANG_ID,
|
||||
PLATFORM_ID, names);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// otherwise, grab names for all languages
|
||||
if (names.Length() == 0) {
|
||||
rv = ReadNames(aNameTable, aNameID, LANG_ALL, PLATFORM_ID, names);
|
||||
rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
|
||||
PLATFORM_ID, names);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
// may be dealing with font that only has Microsoft name entries
|
||||
if (names.Length() == 0) {
|
||||
rv = ReadNames(aNameTable, aNameID, LANG_ID_MICROSOFT_EN_US,
|
||||
rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ID_MICROSOFT_EN_US,
|
||||
PLATFORM_ID_MICROSOFT, names);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// getting really desperate now, take anything!
|
||||
if (names.Length() == 0) {
|
||||
rv = ReadNames(aNameTable, aNameID, LANG_ALL,
|
||||
rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
|
||||
PLATFORM_ID_MICROSOFT, names);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -1322,85 +1333,84 @@ gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen,
|
|||
}
|
||||
|
||||
nsresult
|
||||
gfxFontUtils::ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
|
||||
uint32_t aNameID,
|
||||
int32_t aLangID, int32_t aPlatformID,
|
||||
nsTArray<nsString>& aNames)
|
||||
{
|
||||
uint32_t nameTableLen;
|
||||
const char *nameTable = hb_blob_get_data(aNameTable, &nameTableLen);
|
||||
NS_ASSERTION(nameTableLen != 0, "null name table");
|
||||
NS_ASSERTION(aDataLen != 0, "null name table");
|
||||
|
||||
if (!nameTableLen) {
|
||||
if (!aDataLen) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// -- name table data
|
||||
const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(nameTable);
|
||||
const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
|
||||
|
||||
uint32_t nameCount = nameHeader->count;
|
||||
|
||||
// -- sanity check the number of name records
|
||||
if (uint64_t(nameCount) * sizeof(NameRecord) > nameTableLen) {
|
||||
if (uint64_t(nameCount) * sizeof(NameRecord) > aDataLen) {
|
||||
NS_WARNING("invalid font (name table data)");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// -- iterate through name records
|
||||
const NameRecord *nameRecord
|
||||
= reinterpret_cast<const NameRecord*>(nameTable + sizeof(NameHeader));
|
||||
const NameRecord *nameRecord
|
||||
= reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
|
||||
uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset);
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < nameCount; i++, nameRecord++) {
|
||||
uint32_t platformID;
|
||||
|
||||
|
||||
// skip over unwanted nameID's
|
||||
if (uint32_t(nameRecord->nameID) != aNameID)
|
||||
continue;
|
||||
|
||||
// skip over unwanted platform data
|
||||
platformID = nameRecord->platformID;
|
||||
if (aPlatformID != PLATFORM_ALL
|
||||
if (aPlatformID != PLATFORM_ALL
|
||||
&& uint32_t(nameRecord->platformID) != PLATFORM_ID)
|
||||
continue;
|
||||
|
||||
|
||||
// skip over unwanted languages
|
||||
if (aLangID != LANG_ALL
|
||||
if (aLangID != LANG_ALL
|
||||
&& uint32_t(nameRecord->languageID) != uint32_t(aLangID))
|
||||
continue;
|
||||
|
||||
|
||||
// add name to names array
|
||||
|
||||
|
||||
// -- calculate string location
|
||||
uint32_t namelen = nameRecord->length;
|
||||
uint32_t nameoff = nameRecord->offset; // offset from base of string storage
|
||||
|
||||
if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen)
|
||||
> nameTableLen) {
|
||||
if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen)
|
||||
> aDataLen) {
|
||||
NS_WARNING("invalid font (name table strings)");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// -- decode if necessary and make nsString
|
||||
nsAutoString name;
|
||||
|
||||
DecodeFontName(nameTable + nameStringsBase + nameoff, namelen,
|
||||
|
||||
DecodeFontName(aNameData + nameStringsBase + nameoff, namelen,
|
||||
platformID, uint32_t(nameRecord->encodingID),
|
||||
uint32_t(nameRecord->languageID), name);
|
||||
|
||||
|
||||
uint32_t k, numNames;
|
||||
bool foundName = false;
|
||||
|
||||
|
||||
numNames = aNames.Length();
|
||||
for (k = 0; k < numNames; k++) {
|
||||
if (name.Equals(aNames[k])) {
|
||||
foundName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!foundName)
|
||||
aNames.AppendElement(name);
|
||||
aNames.AppendElement(name);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1421,106 +1431,3 @@ gfxFontUtils::IsCffFont(const uint8_t* aFontData)
|
|||
|
||||
#endif
|
||||
|
||||
NS_IMPL_ISUPPORTS1(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *someData)
|
||||
{
|
||||
if (!nsCRT::strcmp(aTopic, "quit-application")) {
|
||||
mLoader->CancelLoader();
|
||||
} else {
|
||||
NS_NOTREACHED("unexpected notification topic");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
|
||||
{
|
||||
mInterval = aInterval;
|
||||
|
||||
// sanity check
|
||||
if (mState != stateInitial && mState != stateTimerOff) {
|
||||
CancelLoader();
|
||||
}
|
||||
|
||||
// set up timer
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!mTimer) {
|
||||
NS_WARNING("Failure to create font info loader timer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// need an initial delay?
|
||||
uint32_t timerInterval;
|
||||
|
||||
if (aDelay) {
|
||||
mState = stateTimerOnDelay;
|
||||
timerInterval = aDelay;
|
||||
} else {
|
||||
mState = stateTimerOnInterval;
|
||||
timerInterval = mInterval;
|
||||
}
|
||||
|
||||
InitLoader();
|
||||
|
||||
// start timer
|
||||
mTimer->InitWithFuncCallback(LoaderTimerCallback, this, timerInterval,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
if (obs) {
|
||||
mObserver = new ShutdownObserver(this);
|
||||
obs->AddObserver(mObserver, "quit-application", false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::CancelLoader()
|
||||
{
|
||||
if (mState == stateInitial) {
|
||||
return;
|
||||
}
|
||||
mState = stateTimerOff;
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
RemoveShutdownObserver();
|
||||
FinishLoader();
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::LoaderTimerFire()
|
||||
{
|
||||
if (mState == stateTimerOnDelay) {
|
||||
mState = stateTimerOnInterval;
|
||||
mTimer->SetDelay(mInterval);
|
||||
}
|
||||
|
||||
bool done = RunLoader();
|
||||
if (done) {
|
||||
CancelLoader();
|
||||
}
|
||||
}
|
||||
|
||||
gfxFontInfoLoader::~gfxFontInfoLoader()
|
||||
{
|
||||
RemoveShutdownObserver();
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontInfoLoader::RemoveShutdownObserver()
|
||||
{
|
||||
if (mObserver) {
|
||||
nsCOMPtr<nsIObserverService> obs = GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(mObserver, "quit-application");
|
||||
mObserver = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,9 @@
|
|||
#define GFX_FONT_UTILS_H
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
@ -829,15 +826,19 @@ public:
|
|||
|
||||
// read all names matching aNameID, returning in aNames array
|
||||
static nsresult
|
||||
ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
|
||||
int32_t aPlatformID, nsTArray<nsString>& aNames);
|
||||
|
||||
|
||||
// reads English or first name matching aNameID, returning in aName
|
||||
// platform based on OS
|
||||
static nsresult
|
||||
ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
nsString& aName);
|
||||
|
||||
|
||||
static nsresult
|
||||
ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
|
||||
uint32_t aNameID, nsString& aName);
|
||||
|
||||
// convert a name from the raw name table data into an nsString,
|
||||
// provided we know how; return true if successful, or false
|
||||
// if we can't handle the encoding
|
||||
|
@ -916,7 +917,7 @@ public:
|
|||
|
||||
protected:
|
||||
static nsresult
|
||||
ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
|
||||
ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
|
||||
int32_t aLangID, int32_t aPlatformID, nsTArray<nsString>& aNames);
|
||||
|
||||
// convert opentype name-table platform/encoding/language values to a charset name
|
||||
|
@ -939,80 +940,5 @@ protected:
|
|||
static const char* gMSFontNameCharsets[];
|
||||
};
|
||||
|
||||
// helper class for loading in font info spaced out at regular intervals
|
||||
|
||||
class gfxFontInfoLoader {
|
||||
public:
|
||||
|
||||
// state transitions:
|
||||
// initial ---StartLoader with delay---> timer on delay
|
||||
// initial ---StartLoader without delay---> timer on interval
|
||||
// timer on delay ---LoaderTimerFire---> timer on interval
|
||||
// timer on delay ---CancelLoader---> timer off
|
||||
// timer on interval ---CancelLoader---> timer off
|
||||
// timer off ---StartLoader with delay---> timer on delay
|
||||
// timer off ---StartLoader without delay---> timer on interval
|
||||
typedef enum {
|
||||
stateInitial,
|
||||
stateTimerOnDelay,
|
||||
stateTimerOnInterval,
|
||||
stateTimerOff
|
||||
} TimerState;
|
||||
|
||||
gfxFontInfoLoader() :
|
||||
mInterval(0), mState(stateInitial)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~gfxFontInfoLoader();
|
||||
|
||||
// start timer with an initial delay, then call Run method at regular intervals
|
||||
void StartLoader(uint32_t aDelay, uint32_t aInterval);
|
||||
|
||||
// cancel the timer and cleanup
|
||||
void CancelLoader();
|
||||
|
||||
protected:
|
||||
class ShutdownObserver : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
ShutdownObserver(gfxFontInfoLoader *aLoader)
|
||||
: mLoader(aLoader)
|
||||
{ }
|
||||
|
||||
virtual ~ShutdownObserver()
|
||||
{ }
|
||||
|
||||
protected:
|
||||
gfxFontInfoLoader *mLoader;
|
||||
};
|
||||
|
||||
// Init - initialization at start time after initial delay
|
||||
virtual void InitLoader() = 0;
|
||||
|
||||
// Run - called at intervals, return true to indicate done
|
||||
virtual bool RunLoader() = 0;
|
||||
|
||||
// Finish - cleanup after done
|
||||
virtual void FinishLoader() = 0;
|
||||
|
||||
// Timer interval callbacks
|
||||
static void LoaderTimerCallback(nsITimer *aTimer, void *aThis) {
|
||||
gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
|
||||
loader->LoaderTimerFire();
|
||||
}
|
||||
|
||||
void LoaderTimerFire();
|
||||
|
||||
void RemoveShutdownObserver();
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsIObserver> mObserver;
|
||||
uint32_t mInterval;
|
||||
TimerState mState;
|
||||
};
|
||||
|
||||
#endif /* GFX_FONT_UTILS_H */
|
||||
|
|
|
@ -146,7 +146,7 @@ GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
|
|||
}
|
||||
|
||||
nsresult
|
||||
GDIFontEntry::ReadCMAP()
|
||||
GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
||||
{
|
||||
// attempt this once, if errors occur leave a blank cmap
|
||||
if (mCharacterMap) {
|
||||
|
@ -163,22 +163,28 @@ GDIFontEntry::ReadCMAP()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
nsRefPtr<gfxCharacterMap> charmap;
|
||||
nsresult rv;
|
||||
bool unicodeFont = false, symbolFont = false;
|
||||
|
||||
AutoFallibleTArray<uint8_t,16384> cmap;
|
||||
rv = CopyFontTable(kCMAP, cmap);
|
||||
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
|
||||
mUVSOffset,
|
||||
symbolFont))) {
|
||||
mSymbolFont = symbolFont;
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
charmap = new gfxCharacterMap();
|
||||
AutoFallibleTArray<uint8_t,16384> cmap;
|
||||
rv = CopyFontTable(kCMAP, cmap);
|
||||
|
||||
bool unicodeFont = false, symbolFont = false; // currently ignored
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
}
|
||||
mSymbolFont = symbolFont;
|
||||
}
|
||||
mSymbolFont = symbolFont;
|
||||
|
||||
mHasCmapTable = NS_SUCCEEDED(rv);
|
||||
if (mHasCmapTable) {
|
||||
|
@ -512,7 +518,7 @@ GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
|
|||
}
|
||||
|
||||
void
|
||||
GDIFontFamily::FindStyleVariations()
|
||||
GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
|
||||
{
|
||||
if (mHasStyles)
|
||||
return;
|
||||
|
@ -909,3 +915,187 @@ gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
|
|||
aSizes->mFontListSize += aMallocSizeOf(this);
|
||||
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
|
||||
}
|
||||
|
||||
// used to load system-wide font info on off-main thread
|
||||
class GDIFontInfo : public FontInfoData {
|
||||
public:
|
||||
GDIFontInfo(bool aLoadOtherNames,
|
||||
bool aLoadFaceNames,
|
||||
bool aLoadCmaps) :
|
||||
FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
|
||||
{}
|
||||
|
||||
virtual ~GDIFontInfo() {}
|
||||
|
||||
virtual void Load() {
|
||||
mHdc = GetDC(nullptr);
|
||||
SetGraphicsMode(mHdc, GM_ADVANCED);
|
||||
FontInfoData::Load();
|
||||
ReleaseDC(nullptr, mHdc);
|
||||
}
|
||||
|
||||
// loads font data for all members of a given family
|
||||
virtual void LoadFontFamilyData(const nsAString& aFamilyName);
|
||||
|
||||
// callback for GDI EnumFontFamiliesExW call
|
||||
static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW *lpelfe,
|
||||
const NEWTEXTMETRICEXW *nmetrics,
|
||||
DWORD fontType, LPARAM data);
|
||||
|
||||
HDC mHdc;
|
||||
};
|
||||
|
||||
struct EnumerateFontsForFamilyData {
|
||||
EnumerateFontsForFamilyData(const nsAString& aFamilyName,
|
||||
GDIFontInfo& aFontInfo)
|
||||
: mFamilyName(aFamilyName), mFontInfo(aFontInfo)
|
||||
{}
|
||||
|
||||
nsString mFamilyName;
|
||||
nsTArray<nsString> mOtherFamilyNames;
|
||||
GDIFontInfo& mFontInfo;
|
||||
nsString mPreviousFontName;
|
||||
};
|
||||
|
||||
int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
|
||||
const ENUMLOGFONTEXW *lpelfe,
|
||||
const NEWTEXTMETRICEXW *nmetrics,
|
||||
DWORD fontType, LPARAM data)
|
||||
{
|
||||
EnumerateFontsForFamilyData *famData =
|
||||
reinterpret_cast<EnumerateFontsForFamilyData*>(data);
|
||||
HDC hdc = famData->mFontInfo.mHdc;
|
||||
LOGFONTW logFont = lpelfe->elfLogFont;
|
||||
const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
|
||||
|
||||
AutoSelectFont font(hdc, &logFont);
|
||||
if (!font.IsValid()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
FontFaceData fontData;
|
||||
nsDependentString fontName(lpelfe->elfFullName);
|
||||
|
||||
// callback called for each style-charset so return if style already seen
|
||||
if (fontName.Equals(famData->mPreviousFontName)) {
|
||||
return 1;
|
||||
}
|
||||
famData->mPreviousFontName = fontName;
|
||||
famData->mFontInfo.mLoadStats.fonts++;
|
||||
|
||||
// read name table info
|
||||
bool nameDataLoaded = false;
|
||||
if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
|
||||
uint32_t kNAME =
|
||||
NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
|
||||
uint32_t nameSize;
|
||||
nsAutoTArray<uint8_t, 1024> nameData;
|
||||
|
||||
nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
|
||||
if (nameSize != GDI_ERROR &&
|
||||
nameSize > 0 &&
|
||||
nameData.SetLength(nameSize)) {
|
||||
::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);
|
||||
|
||||
// face names
|
||||
if (famData->mFontInfo.mLoadFaceNames) {
|
||||
gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
|
||||
gfxFontUtils::NAME_ID_FULL,
|
||||
fontData.mFullName);
|
||||
gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
|
||||
gfxFontUtils::NAME_ID_POSTSCRIPT,
|
||||
fontData.mPostscriptName);
|
||||
nameDataLoaded = true;
|
||||
famData->mFontInfo.mLoadStats.facenames++;
|
||||
}
|
||||
|
||||
// other family names
|
||||
if (famData->mFontInfo.mLoadOtherNames) {
|
||||
gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName,
|
||||
(const char*)(nameData.Elements()),
|
||||
nameSize,
|
||||
famData->mOtherFamilyNames,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read cmap
|
||||
bool cmapLoaded = false;
|
||||
gfxWindowsFontType feType =
|
||||
GDIFontEntry::DetermineFontType(metrics, fontType);
|
||||
if (famData->mFontInfo.mLoadCmaps &&
|
||||
(feType == GFX_FONT_TYPE_PS_OPENTYPE ||
|
||||
feType == GFX_FONT_TYPE_TT_OPENTYPE ||
|
||||
feType == GFX_FONT_TYPE_TRUETYPE))
|
||||
{
|
||||
uint32_t kCMAP =
|
||||
NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
|
||||
uint32_t cmapSize;
|
||||
nsAutoTArray<uint8_t, 1024> cmapData;
|
||||
|
||||
cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
|
||||
if (cmapSize != GDI_ERROR &&
|
||||
cmapSize > 0 &&
|
||||
cmapData.SetLength(cmapSize)) {
|
||||
::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
|
||||
bool cmapLoaded = false;
|
||||
bool unicodeFont = false, symbolFont = false;
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
uint32_t offset;
|
||||
|
||||
if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(),
|
||||
cmapSize, *charmap,
|
||||
offset, unicodeFont,
|
||||
symbolFont))) {
|
||||
fontData.mCharacterMap = charmap;
|
||||
fontData.mUVSOffset = offset;
|
||||
fontData.mSymbolFont = symbolFont;
|
||||
cmapLoaded = true;
|
||||
famData->mFontInfo.mLoadStats.cmaps++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmapLoaded || nameDataLoaded) {
|
||||
famData->mFontInfo.mFontFaceData.Put(fontName, fontData);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
GDIFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
|
||||
{
|
||||
// iterate over the family
|
||||
LOGFONTW logFont;
|
||||
memset(&logFont, 0, sizeof(LOGFONTW));
|
||||
logFont.lfCharSet = DEFAULT_CHARSET;
|
||||
logFont.lfPitchAndFamily = 0;
|
||||
uint32_t l = std::min<uint32_t>(aFamilyName.Length(), LF_FACESIZE - 1);
|
||||
memcpy(logFont.lfFaceName, aFamilyName.BeginReading(), l * sizeof(char16_t));
|
||||
|
||||
EnumerateFontsForFamilyData data(aFamilyName, *this);
|
||||
|
||||
EnumFontFamiliesExW(mHdc, &logFont,
|
||||
(FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
|
||||
(LPARAM)(&data), 0);
|
||||
|
||||
// if found other names, insert them
|
||||
if (data.mOtherFamilyNames.Length() != 0) {
|
||||
mOtherFamilyNames.Put(aFamilyName, data.mOtherFamilyNames);
|
||||
mLoadStats.othernames += data.mOtherFamilyNames.Length();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<FontInfoData>
|
||||
gfxGDIFontList::CreateFontInfoData()
|
||||
{
|
||||
bool loadCmaps = !UsesSystemFallback() ||
|
||||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
|
||||
|
||||
nsRefPtr<GDIFontInfo> fi =
|
||||
new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
|
||||
|
||||
return fi.forget();
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ class GDIFontEntry : public gfxFontEntry
|
|||
public:
|
||||
LPLOGFONTW GetLogFont() { return &mLogFont; }
|
||||
|
||||
nsresult ReadCMAP();
|
||||
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
virtual bool IsSymbolFont();
|
||||
|
||||
|
@ -294,7 +294,7 @@ public:
|
|||
GDIFontFamily(nsAString &aName) :
|
||||
gfxFontFamily(aName) {}
|
||||
|
||||
virtual void FindStyleVariations();
|
||||
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
private:
|
||||
static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
|
||||
|
@ -339,6 +339,8 @@ private:
|
|||
DWORD fontType,
|
||||
LPARAM lParam);
|
||||
|
||||
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
|
||||
|
||||
typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontTable;
|
||||
|
||||
FontTable mFontSubstitutes;
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
|
||||
FontListSizes* aSizes) const;
|
||||
|
||||
nsresult ReadCMAP();
|
||||
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
|
||||
|
||||
bool RequiresAATLayout() const { return mRequiresAAT; }
|
||||
|
||||
|
@ -116,6 +116,8 @@ private:
|
|||
|
||||
virtual bool UsesSystemFallback() { return true; }
|
||||
|
||||
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
|
||||
|
||||
enum {
|
||||
kATSGenerationInitial = -1
|
||||
};
|
||||
|
|
|
@ -222,30 +222,38 @@ SupportsScriptInGSUB(gfxFontEntry* aFontEntry, const hb_tag_t* aScriptTags)
|
|||
}
|
||||
|
||||
nsresult
|
||||
MacOSFontEntry::ReadCMAP()
|
||||
MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
||||
{
|
||||
// attempt this once, if errors occur leave a blank cmap
|
||||
if (mCharacterMap) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
|
||||
AutoTable cmapTable(this, kCMAP);
|
||||
nsRefPtr<gfxCharacterMap> charmap;
|
||||
nsresult rv;
|
||||
bool symbolFont;
|
||||
|
||||
if (cmapTable) {
|
||||
bool unicodeFont = false, symbolFont = false; // currently ignored
|
||||
|
||||
uint32_t cmapLen;
|
||||
const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
|
||||
rv = gfxFontUtils::ReadCMAP((const uint8_t*)cmapData, cmapLen,
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
|
||||
mUVSOffset,
|
||||
symbolFont))) {
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
||||
charmap = new gfxCharacterMap();
|
||||
AutoTable cmapTable(this, kCMAP);
|
||||
|
||||
if (cmapTable) {
|
||||
bool unicodeFont = false, symbolFont = false; // currently ignored
|
||||
uint32_t cmapLen;
|
||||
const uint8_t* cmapData =
|
||||
reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
|
||||
&cmapLen));
|
||||
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
|
||||
*charmap, mUVSOffset,
|
||||
unicodeFont, symbolFont);
|
||||
} else {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
|
||||
|
@ -446,7 +454,7 @@ public:
|
|||
|
||||
virtual void LocalizedName(nsAString& aLocalizedName);
|
||||
|
||||
virtual void FindStyleVariations();
|
||||
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -474,7 +482,7 @@ gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
|
|||
}
|
||||
|
||||
void
|
||||
gfxMacFontFamily::FindStyleVariations()
|
||||
gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
|
||||
{
|
||||
if (mHasStyles)
|
||||
return;
|
||||
|
@ -678,30 +686,48 @@ gfxMacPlatformFontList::InitFontList()
|
|||
gfxPlatformFontList::InitFontList();
|
||||
|
||||
// iterate over available families
|
||||
NSEnumerator *families = [[sFontManager availableFontFamilies]
|
||||
objectEnumerator]; // returns "canonical", non-localized family name
|
||||
|
||||
nsAutoString availableFamilyName;
|
||||
CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
|
||||
NSString *availableFamily = nil;
|
||||
while ((availableFamily = [families nextObject])) {
|
||||
// iterate over families
|
||||
uint32_t i, numFamilies;
|
||||
|
||||
// make a nsString
|
||||
GetStringForNSString(availableFamily, availableFamilyName);
|
||||
numFamilies = CFArrayGetCount(familyNames);
|
||||
for (i = 0; i < numFamilies; i++) {
|
||||
CFStringRef family = (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
|
||||
|
||||
// CTFontManager includes weird internal family names and
|
||||
// LastResort, skip over those
|
||||
if (!family ||
|
||||
::CFStringHasPrefix(family, CFSTR(".")) ||
|
||||
CFStringCompare(family, CFSTR("LastResort"),
|
||||
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoTArray<UniChar, 1024> buffer;
|
||||
CFIndex len = ::CFStringGetLength(family);
|
||||
buffer.SetLength(len+1);
|
||||
::CFStringGetCharacters(family, ::CFRangeMake(0, len),
|
||||
buffer.Elements());
|
||||
buffer[len] = 0;
|
||||
nsAutoString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
|
||||
|
||||
// create a family entry
|
||||
gfxFontFamily *familyEntry = new gfxMacFontFamily(availableFamilyName);
|
||||
gfxFontFamily *familyEntry = new gfxMacFontFamily(familyName);
|
||||
if (!familyEntry) break;
|
||||
|
||||
// add the family entry to the hash table
|
||||
ToLowerCase(availableFamilyName);
|
||||
mFontFamilies.Put(availableFamilyName, familyEntry);
|
||||
ToLowerCase(familyName);
|
||||
mFontFamilies.Put(familyName, familyEntry);
|
||||
|
||||
// check the bad underline blacklist
|
||||
if (mBadUnderlineFamilyNames.Contains(availableFamilyName))
|
||||
if (mBadUnderlineFamilyNames.Contains(familyName))
|
||||
familyEntry->SetBadUnderlineFamily();
|
||||
}
|
||||
|
||||
CFRelease(familyNames);
|
||||
|
||||
InitSingleFaceList();
|
||||
|
||||
// to avoid full search of font name tables, seed the other names table with localized names from
|
||||
|
@ -996,3 +1022,139 @@ gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
|
|||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// used to load system-wide font info on off-main thread
|
||||
class MacFontInfo : public FontInfoData {
|
||||
public:
|
||||
MacFontInfo(bool aLoadOtherNames,
|
||||
bool aLoadFaceNames,
|
||||
bool aLoadCmaps) :
|
||||
FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
|
||||
{}
|
||||
|
||||
virtual ~MacFontInfo() {}
|
||||
|
||||
virtual void Load() {
|
||||
nsAutoreleasePool localPool;
|
||||
FontInfoData::Load();
|
||||
}
|
||||
|
||||
// loads font data for all members of a given family
|
||||
virtual void LoadFontFamilyData(const nsAString& aFamilyName);
|
||||
};
|
||||
|
||||
void
|
||||
MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
|
||||
{
|
||||
// family name ==> CTFontDescriptor
|
||||
NSString *famName = GetNSStringForString(aFamilyName);
|
||||
CFStringRef family = CFStringRef(famName);
|
||||
|
||||
CFMutableDictionaryRef attr =
|
||||
CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
|
||||
CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
|
||||
CFRelease(attr);
|
||||
CFArrayRef matchingFonts =
|
||||
CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
|
||||
CFRelease(fd);
|
||||
if (!matchingFonts) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsString> otherFamilyNames;
|
||||
bool hasOtherFamilyNames = true;
|
||||
|
||||
// iterate over faces in the family
|
||||
int f, numFaces = (int) CFArrayGetCount(matchingFonts);
|
||||
for (f = 0; f < numFaces; f++) {
|
||||
mLoadStats.fonts++;
|
||||
|
||||
CTFontDescriptorRef faceDesc =
|
||||
(CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
|
||||
if (!faceDesc) {
|
||||
continue;
|
||||
}
|
||||
CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
|
||||
0.0, nullptr);
|
||||
|
||||
if (mLoadCmaps) {
|
||||
// face name
|
||||
CFStringRef faceName = (CFStringRef)
|
||||
CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
|
||||
|
||||
nsAutoTArray<UniChar, 1024> buffer;
|
||||
CFIndex len = CFStringGetLength(faceName);
|
||||
buffer.SetLength(len+1);
|
||||
CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
|
||||
buffer.Elements());
|
||||
buffer[len] = 0;
|
||||
nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
|
||||
len);
|
||||
|
||||
// load the cmap data
|
||||
FontFaceData fontData;
|
||||
CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
|
||||
kCTFontTableOptionNoOptions);
|
||||
if (cmapTable) {
|
||||
bool unicodeFont = false, symbolFont = false; // ignored
|
||||
const uint8_t *cmapData =
|
||||
(const uint8_t*)CFDataGetBytePtr(cmapTable);
|
||||
uint32_t cmapLen = CFDataGetLength(cmapTable);
|
||||
nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
|
||||
uint32_t offset;
|
||||
nsresult rv;
|
||||
|
||||
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
|
||||
unicodeFont, symbolFont);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
fontData.mCharacterMap = charmap;
|
||||
fontData.mUVSOffset = offset;
|
||||
fontData.mSymbolFont = symbolFont;
|
||||
mLoadStats.cmaps++;
|
||||
}
|
||||
CFRelease(cmapTable);
|
||||
}
|
||||
|
||||
mFontFaceData.Put(fontName, fontData);
|
||||
CFRelease(faceName);
|
||||
}
|
||||
|
||||
if (mLoadOtherNames && hasOtherFamilyNames) {
|
||||
CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
|
||||
kCTFontTableOptionNoOptions);
|
||||
if (nameTable) {
|
||||
const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
|
||||
uint32_t nameLen = CFDataGetLength(nameTable);
|
||||
gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
|
||||
nameData, nameLen,
|
||||
otherFamilyNames,
|
||||
false);
|
||||
hasOtherFamilyNames = otherFamilyNames.Length() != 0;
|
||||
CFRelease(nameTable);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(fontRef);
|
||||
}
|
||||
CFRelease(matchingFonts);
|
||||
|
||||
// if found other names, insert them in the hash table
|
||||
if (otherFamilyNames.Length() != 0) {
|
||||
mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
|
||||
mLoadStats.othernames += otherFamilyNames.Length();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<FontInfoData>
|
||||
gfxMacPlatformFontList::CreateFontInfoData()
|
||||
{
|
||||
bool loadCmaps = !UsesSystemFallback() ||
|
||||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
|
||||
|
||||
nsRefPtr<MacFontInfo> fi =
|
||||
new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
|
||||
return fi.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -558,6 +558,25 @@ static void LogRegistryEvent(const wchar_t *msg)
|
|||
}
|
||||
#endif
|
||||
|
||||
gfxFontFamily*
|
||||
gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
|
||||
{
|
||||
if (aFamily && !aFamily->HasStyles()) {
|
||||
aFamily->FindStyleVariations();
|
||||
aFamily->CheckForSimpleFamily();
|
||||
}
|
||||
|
||||
if (aFamily && aFamily->GetFontList().Length() == 0) {
|
||||
// failed to load any faces for this family, so discard it
|
||||
nsAutoString key;
|
||||
GenerateFontListKey(aFamily->Name(), key);
|
||||
mFontFamilies.Remove(key);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return aFamily;
|
||||
}
|
||||
|
||||
gfxFontFamily*
|
||||
gfxPlatformFontList::FindFamily(const nsAString& aFamily)
|
||||
{
|
||||
|
@ -569,12 +588,12 @@ gfxPlatformFontList::FindFamily(const nsAString& aFamily)
|
|||
|
||||
// lookup in canonical (i.e. English) family name list
|
||||
if ((familyEntry = mFontFamilies.GetWeak(key))) {
|
||||
return familyEntry;
|
||||
return CheckFamily(familyEntry);
|
||||
}
|
||||
|
||||
// lookup in other family names list (mostly localized names)
|
||||
if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
|
||||
return familyEntry;
|
||||
return CheckFamily(familyEntry);
|
||||
}
|
||||
|
||||
// name not found and other family names not yet fully initialized so
|
||||
|
@ -585,7 +604,7 @@ gfxPlatformFontList::FindFamily(const nsAString& aFamily)
|
|||
if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
|
||||
InitOtherFamilyNames();
|
||||
if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
|
||||
return familyEntry;
|
||||
return CheckFamily(familyEntry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,49 +724,59 @@ gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
|
|||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator AppendFamilyToList(nsStringHashKey::KeyType aKey,
|
||||
nsRefPtr<gfxFontFamily>& aFamilyEntry,
|
||||
void *aUserArg)
|
||||
{
|
||||
nsTArray<nsString> *familyNames = static_cast<nsTArray<nsString> *>(aUserArg);
|
||||
familyNames->AppendElement(aFamilyEntry->Name());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
|
||||
{
|
||||
mFontFamilies.Enumerate(AppendFamilyToList, &aFontFamilyNames);
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatformFontList::InitLoader()
|
||||
{
|
||||
GetFontFamilyList(mFontFamiliesToLoad);
|
||||
GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
|
||||
mStartIndex = 0;
|
||||
mNumFamilies = mFontFamiliesToLoad.Length();
|
||||
mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
|
||||
memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
|
||||
}
|
||||
|
||||
#define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms
|
||||
|
||||
bool
|
||||
gfxPlatformFontList::RunLoader()
|
||||
gfxPlatformFontList::LoadFontInfo()
|
||||
{
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
uint32_t i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
|
||||
uint32_t i, endIndex = mNumFamilies;
|
||||
bool loadCmaps = !UsesSystemFallback() ||
|
||||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
|
||||
|
||||
// for each font family, load in various font info
|
||||
for (i = mStartIndex; i < endIndex; i++) {
|
||||
gfxFontFamily* familyEntry = mFontFamiliesToLoad[i];
|
||||
nsAutoString key;
|
||||
gfxFontFamily *familyEntry;
|
||||
GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
|
||||
|
||||
// find all faces that are members of this family
|
||||
familyEntry->FindStyleVariations();
|
||||
if (familyEntry->GetFontList().Length() == 0) {
|
||||
// failed to load any faces for this family, so discard it
|
||||
nsAutoString key;
|
||||
GenerateFontListKey(familyEntry->Name(), key);
|
||||
mFontFamilies.Remove(key);
|
||||
// lookup in canonical (i.e. English) family name list
|
||||
if (!(familyEntry = mFontFamilies.GetWeak(key))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// read in face names
|
||||
familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo);
|
||||
|
||||
// load the cmaps if needed
|
||||
if (loadCmaps) {
|
||||
familyEntry->ReadAllCMAPs();
|
||||
familyEntry->ReadAllCMAPs(mFontInfo);
|
||||
}
|
||||
|
||||
// read in face names
|
||||
familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames());
|
||||
|
||||
// check whether the family can be considered "simple" for style matching
|
||||
familyEntry->CheckForSimpleFamily();
|
||||
|
||||
// limit the time spent reading fonts in one pass
|
||||
TimeDuration elapsed = TimeStamp::Now() - start;
|
||||
if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
|
||||
|
@ -758,15 +787,42 @@ gfxPlatformFontList::RunLoader()
|
|||
}
|
||||
|
||||
mStartIndex = endIndex;
|
||||
bool done = mStartIndex >= mNumFamilies;
|
||||
|
||||
return (mStartIndex >= mNumFamilies);
|
||||
#ifdef PR_LOGGING
|
||||
if (LOG_FONTINIT_ENABLED()) {
|
||||
TimeDuration elapsed = TimeStamp::Now() - start;
|
||||
LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
|
||||
elapsed.ToMilliseconds(), (done ? "true" : "false")));
|
||||
}
|
||||
#endif
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatformFontList::FinishLoader()
|
||||
gfxPlatformFontList::CleanupLoader()
|
||||
{
|
||||
mFontFamiliesToLoad.Clear();
|
||||
mNumFamilies = 0;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (LOG_FONTINIT_ENABLED() && mFontInfo) {
|
||||
LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
|
||||
"%d families %d fonts %d cmaps "
|
||||
"%d facenames %d othernames",
|
||||
mLoadTime.ToMilliseconds(),
|
||||
mFontInfo->mLoadStats.families,
|
||||
mFontInfo->mLoadStats.fonts,
|
||||
mFontInfo->mLoadStats.cmaps,
|
||||
mFontInfo->mLoadStats.facenames,
|
||||
mFontInfo->mLoadStats.othernames));
|
||||
}
|
||||
#endif
|
||||
|
||||
mOtherFamilyNamesInitialized = true;
|
||||
mFaceNamesInitialized = true;
|
||||
gfxFontInfoLoader::CleanupLoader();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsTHashtable.h"
|
||||
|
||||
#include "gfxFontUtils.h"
|
||||
#include "gfxFontInfoLoader.h"
|
||||
#include "gfxFont.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
|
@ -81,7 +82,7 @@ struct FontListSizes {
|
|||
uint32_t mCharMapsSize; // memory used for cmap coverage info
|
||||
};
|
||||
|
||||
class gfxPlatformFontList : protected gfxFontInfoLoader
|
||||
class gfxPlatformFontList : public gfxFontInfoLoader
|
||||
{
|
||||
public:
|
||||
static gfxPlatformFontList* PlatformFontList() {
|
||||
|
@ -210,6 +211,9 @@ protected:
|
|||
// if system fallback is used, no need to load all cmaps
|
||||
virtual bool UsesSystemFallback() { return false; }
|
||||
|
||||
// verifies that a family contains a non-zero font count
|
||||
gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
|
||||
|
||||
// separate initialization for reading in name tables, since this is expensive
|
||||
void InitOtherFamilyNames();
|
||||
|
||||
|
@ -240,10 +244,12 @@ protected:
|
|||
nsRefPtr<gfxFontFamily>& aFamilyEntry,
|
||||
void* aUserArg);
|
||||
|
||||
virtual void GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames);
|
||||
|
||||
// gfxFontInfoLoader overrides, used to load in font cmaps
|
||||
virtual void InitLoader();
|
||||
virtual bool RunLoader();
|
||||
virtual void FinishLoader();
|
||||
virtual bool LoadFontInfo();
|
||||
virtual void CleanupLoader();
|
||||
|
||||
// read the loader initialization prefs, and start it
|
||||
void GetPrefsAndStartLoader();
|
||||
|
|
|
@ -20,6 +20,7 @@ EXPORTS += [
|
|||
'gfxFont.h',
|
||||
'gfxFontConstants.h',
|
||||
'gfxFontFeatures.h',
|
||||
'gfxFontInfoLoader.h',
|
||||
'gfxFontTest.h',
|
||||
'gfxFontUtils.h',
|
||||
'gfxGradientCache.h',
|
||||
|
@ -244,6 +245,7 @@ UNIFIED_SOURCES += [
|
|||
'gfxCachedTempSurface.cpp',
|
||||
'gfxContext.cpp',
|
||||
'gfxFontFeatures.cpp',
|
||||
'gfxFontInfoLoader.cpp',
|
||||
'gfxFontMissingGlyphs.cpp',
|
||||
'gfxFontTest.cpp',
|
||||
'gfxGradientCache.cpp',
|
||||
|
|
|
@ -293,20 +293,20 @@ jsval
|
|||
jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedValue val(cx, jsdval->val);
|
||||
if (!JSVAL_IS_PRIMITIVE(val)) {
|
||||
JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(val));
|
||||
obj = JS_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val));
|
||||
if (!val.isPrimitive()) {
|
||||
JS::RootedObject obj(cx, &val.toObject());
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
obj = JS_ObjectToOuterObject(cx, obj);
|
||||
if (!obj)
|
||||
{
|
||||
JS_ClearPendingException(cx);
|
||||
val = JSVAL_NULL;
|
||||
}
|
||||
else
|
||||
val = OBJECT_TO_JSVAL(obj);
|
||||
val = JS::ObjectValue(*obj);
|
||||
}
|
||||
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ using mozilla::IsNaN;
|
|||
using mozilla::Move;
|
||||
using mozilla::ArrayLength;
|
||||
using JS::DoubleNaNValue;
|
||||
using JS::ForOfIterator;
|
||||
|
||||
|
||||
/*** OrderedHashTable ****************************************************************************/
|
||||
|
|
|
@ -1053,7 +1053,7 @@ InitInt64Class(JSContext* cx,
|
|||
const JSFunctionSpec* static_fs)
|
||||
{
|
||||
// Init type class and constructor
|
||||
RootedObject prototype(cx, JS_InitClass(cx, parent, nullptr, clasp, construct,
|
||||
RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct,
|
||||
0, nullptr, fs, nullptr, static_fs));
|
||||
if (!prototype)
|
||||
return nullptr;
|
||||
|
@ -5182,7 +5182,7 @@ StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args)
|
|||
}
|
||||
|
||||
MOZ_ASSERT(args.rval().isObject());
|
||||
MOZ_ASSERT(JS_IsArrayObject(cx, &args.rval().toObject()));
|
||||
MOZ_ASSERT(JS_IsArrayObject(cx, args.rval()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,9 @@ CheckMarkedThing(JSTracer *trc, T *thing)
|
|||
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && AsGCMarker(trc)->getMarkColor() == GRAY,
|
||||
!thing->zone()->isGCMarkingBlack() || rt->isAtomsZone(thing->zone()));
|
||||
|
||||
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc),
|
||||
!(thing->zone()->isGCSweeping() || thing->zone()->isGCFinished()));
|
||||
|
||||
/*
|
||||
* Try to assert that the thing is allocated. This is complicated by the
|
||||
* fact that allocated things may still contain the poison pattern if that
|
||||
|
|
|
@ -590,13 +590,15 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
|
|||
|
||||
/* ArrayBuffer stores byte-length, not Value count. */
|
||||
if (src->is<ArrayBufferObject>()) {
|
||||
size_t nbytes = sizeof(ObjectElements) + srcHeader->initializedLength;
|
||||
size_t nbytes;
|
||||
if (src->hasDynamicElements()) {
|
||||
nbytes = sizeof(ObjectElements) + srcHeader->initializedLength;
|
||||
dstHeader = static_cast<ObjectElements *>(zone->malloc_(nbytes));
|
||||
if (!dstHeader)
|
||||
CrashAtUnhandlableOOM("Failed to allocate array buffer elements while tenuring.");
|
||||
} else {
|
||||
dst->setFixedElements();
|
||||
nbytes = GetGCKindSlots(dst->tenuredGetAllocKind()) * sizeof(HeapSlot);
|
||||
dstHeader = dst->getElementsHeader();
|
||||
}
|
||||
js_memcpy(dstHeader, srcHeader, nbytes);
|
||||
|
|
|
@ -137,20 +137,28 @@ Zone::sweepBreakpoints(FreeOp *fop)
|
|||
gcstats::AutoPhase ap1(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES);
|
||||
gcstats::AutoPhase ap2(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES_BREAKPOINT);
|
||||
|
||||
JS_ASSERT(isGCSweeping());
|
||||
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
JS_ASSERT(script->zone()->isGCSweeping());
|
||||
if (!script->hasAnyBreakpointsOrStepMode())
|
||||
continue;
|
||||
|
||||
bool scriptGone = IsScriptAboutToBeFinalized(&script);
|
||||
JS_ASSERT(script == i.get<JSScript>());
|
||||
for (unsigned i = 0; i < script->length(); i++) {
|
||||
BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i));
|
||||
if (!site)
|
||||
continue;
|
||||
|
||||
Breakpoint *nextbp;
|
||||
for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInSite();
|
||||
if (scriptGone || IsObjectAboutToBeFinalized(&bp->debugger->toJSObjectRef()))
|
||||
HeapPtrObject& dbgobj = bp->debugger->toJSObjectRef();
|
||||
JS_ASSERT(dbgobj->zone()->isGCSweeping());
|
||||
bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj);
|
||||
JS_ASSERT_IF(!dying, bp->getHandler()->isMarked());
|
||||
if (dying)
|
||||
bp->destroy(fop);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
var g = newGlobal();
|
||||
g.eval("function f() {\n" +
|
||||
" debugger;\n" +
|
||||
"}\n")
|
||||
|
||||
var dbg = new Debugger(g);
|
||||
var handler = {};
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
frame.script.setBreakpoint(0, {});
|
||||
};
|
||||
|
||||
// create breakpoint
|
||||
g.f()
|
||||
|
||||
// drop our references to things
|
||||
handler = undefined;
|
||||
dbg.onDebuggerStatement = undefined;
|
||||
|
||||
dbg.removeAllDebuggees();
|
||||
|
||||
gc();
|
||||
|
||||
//create garbage to trigger a minor GC
|
||||
var x;
|
||||
for (var i = 0; i < 100; ++i)
|
||||
x = {};
|
||||
|
||||
gc();
|
|
@ -0,0 +1,14 @@
|
|||
g = Function("", "for (var i = 0; i < 0; ++i) { eval('this.arg'+0 +'=arg'+0); }");
|
||||
Math.abs(undefined);
|
||||
gczeal(2,300);
|
||||
evaluate("\
|
||||
var toFloat32 = (function() {\
|
||||
var f32 = new Float32Array(1);\
|
||||
function f(x) f32[0] = x;\
|
||||
return f;\
|
||||
})();\
|
||||
for (var i = 0; i < 64; ++i) {\
|
||||
var p = Math.pow(2, i) + 1;\
|
||||
g(toFloat32(p));\
|
||||
toFloat32(-p);\
|
||||
}");
|
|
@ -56,7 +56,7 @@ static const JSFunctionSpec ptestFunctions[] = {
|
|||
|
||||
BEGIN_TEST(testClassGetter_isCalled)
|
||||
{
|
||||
CHECK(JS_InitClass(cx, global, nullptr, &ptestClass, PTest, 0,
|
||||
CHECK(JS_InitClass(cx, global, js::NullPtr(), &ptestClass, PTest, 0,
|
||||
nullptr, ptestFunctions, nullptr, nullptr));
|
||||
|
||||
EXEC("function check() { var o = new PTest(); o.test_fn(); o.test_value1; o.test_value2; o.test_value1; }");
|
||||
|
|
|
@ -62,7 +62,7 @@ IterClassConstructor(JSContext *cx, unsigned argc, jsval *vp)
|
|||
|
||||
BEGIN_TEST(testCustomIterator_bug612523)
|
||||
{
|
||||
CHECK(JS_InitClass(cx, global, nullptr, Jsvalify(&HasCustomIterClass),
|
||||
CHECK(JS_InitClass(cx, global, js::NullPtr(), Jsvalify(&HasCustomIterClass),
|
||||
IterClassConstructor, 0, nullptr, nullptr, nullptr, nullptr));
|
||||
|
||||
JS::RootedValue result(cx);
|
||||
|
|
|
@ -28,7 +28,7 @@ ObjectEmulatingUndefinedConstructor(JSContext *cx, unsigned argc, jsval *vp)
|
|||
|
||||
BEGIN_TEST(testObjectEmulatingUndefined_truthy)
|
||||
{
|
||||
CHECK(JS_InitClass(cx, global, nullptr, &ObjectEmulatingUndefinedClass,
|
||||
CHECK(JS_InitClass(cx, global, js::NullPtr(), &ObjectEmulatingUndefinedClass,
|
||||
ObjectEmulatingUndefinedConstructor, 0,
|
||||
nullptr, nullptr, nullptr, nullptr));
|
||||
|
||||
|
@ -54,7 +54,7 @@ END_TEST(testObjectEmulatingUndefined_truthy)
|
|||
|
||||
BEGIN_TEST(testObjectEmulatingUndefined_equal)
|
||||
{
|
||||
CHECK(JS_InitClass(cx, global, nullptr, &ObjectEmulatingUndefinedClass,
|
||||
CHECK(JS_InitClass(cx, global, js::NullPtr(), &ObjectEmulatingUndefinedClass,
|
||||
ObjectEmulatingUndefinedConstructor, 0,
|
||||
nullptr, nullptr, nullptr, nullptr));
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ initialize(JSContext *cx)
|
|||
{
|
||||
js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 10);
|
||||
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
return JS_InitClass(cx, global, nullptr, &ptestClass, Prof, 0,
|
||||
return JS_InitClass(cx, global, js::NullPtr(), &ptestClass, Prof, 0,
|
||||
nullptr, ptestFunctions, nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ TestPlainTypedArray(JSContext *cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
template<JSObject *CreateWithBuffer(JSContext *, JSObject *, uint32_t, int32_t),
|
||||
JSObject *CreateFromArray(JSContext *, JSObject *),
|
||||
template<JSObject *CreateWithBuffer(JSContext *, JS::HandleObject, uint32_t, int32_t),
|
||||
JSObject *CreateFromArray(JSContext *, JS::HandleObject),
|
||||
typename Element,
|
||||
Element *GetData(JSObject *)>
|
||||
bool
|
||||
|
|
|
@ -2294,13 +2294,11 @@ JS_ConvertStub(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_InitClass(JSContext *cx, JSObject *objArg, JSObject *parent_protoArg,
|
||||
JS_InitClass(JSContext *cx, HandleObject obj, HandleObject parent_proto,
|
||||
const JSClass *clasp, JSNative constructor, unsigned nargs,
|
||||
const JSPropertySpec *ps, const JSFunctionSpec *fs,
|
||||
const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
RootedObject parent_proto(cx, parent_protoArg);
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj, parent_proto);
|
||||
|
@ -3819,13 +3817,21 @@ JS_NewArrayObject(JSContext *cx, int length, jsval *vector)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_IsArrayObject(JSContext *cx, JSObject *objArg)
|
||||
JS_IsArrayObject(JSContext *cx, JS::HandleObject obj)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
assertSameCompartment(cx, obj);
|
||||
return ObjectClassIs(obj, ESClass_Array, cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_IsArrayObject(JSContext *cx, JS::HandleValue value)
|
||||
{
|
||||
if (!value.isObject())
|
||||
return false;
|
||||
RootedObject obj(cx, &value.toObject());
|
||||
return JS_IsArrayObject(cx, obj);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_GetArrayLength(JSContext *cx, HandleObject obj, uint32_t *lengthp)
|
||||
{
|
||||
|
|
|
@ -2526,7 +2526,7 @@ struct JSFunctionSpec {
|
|||
{name, {call, info}, nargs, flags, selfHostedName}
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||
JS_InitClass(JSContext *cx, JS::HandleObject obj, JS::HandleObject parent_proto,
|
||||
const JSClass *clasp, JSNative constructor, unsigned nargs,
|
||||
const JSPropertySpec *ps, const JSFunctionSpec *fs,
|
||||
const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs);
|
||||
|
@ -3086,7 +3086,10 @@ extern JS_PUBLIC_API(JSObject *)
|
|||
JS_NewArrayObject(JSContext *cx, int length, jsval *vector);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_IsArrayObject(JSContext *cx, JSObject *obj);
|
||||
JS_IsArrayObject(JSContext *cx, JS::HandleValue value);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_IsArrayObject(JSContext *cx, JS::HandleObject obj);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_GetArrayLength(JSContext *cx, JS::Handle<JSObject*> obj, uint32_t *lengthp);
|
||||
|
@ -4807,6 +4810,63 @@ struct AsmJSCacheOps
|
|||
extern JS_PUBLIC_API(void)
|
||||
SetAsmJSCacheOps(JSRuntime *rt, const AsmJSCacheOps *callbacks);
|
||||
|
||||
/*
|
||||
* Convenience class for imitating a JS level for-of loop. Typical usage:
|
||||
*
|
||||
* ForOfIterator it(cx);
|
||||
* if (!it.init(iterable))
|
||||
* return false;
|
||||
* RootedValue val(cx);
|
||||
* while (true) {
|
||||
* bool done;
|
||||
* if (!it.next(&val, &done))
|
||||
* return false;
|
||||
* if (done)
|
||||
* break;
|
||||
* if (!DoStuff(cx, val))
|
||||
* return false;
|
||||
* }
|
||||
*/
|
||||
class MOZ_STACK_CLASS JS_PUBLIC_API(ForOfIterator) {
|
||||
protected:
|
||||
JSContext *cx_;
|
||||
JS::RootedObject iterator;
|
||||
|
||||
ForOfIterator(const ForOfIterator &) MOZ_DELETE;
|
||||
ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE;
|
||||
|
||||
public:
|
||||
ForOfIterator(JSContext *cx) : cx_(cx), iterator(cx) { }
|
||||
|
||||
enum NonIterableBehavior {
|
||||
ThrowOnNonIterable,
|
||||
AllowNonIterable
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the iterator. If AllowNonIterable is passed then if iterable
|
||||
* does not have a callable @@iterator init() will just return true instead
|
||||
* of throwing. Callers should then check valueIsIterable() before
|
||||
* continuing with the iteration.
|
||||
*/
|
||||
bool init(JS::HandleValue iterable,
|
||||
NonIterableBehavior nonIterableBehavior = ThrowOnNonIterable);
|
||||
|
||||
/*
|
||||
* Get the next value from the iterator. If false *done is true
|
||||
* after this call, do not examine val.
|
||||
*/
|
||||
bool next(JS::MutableHandleValue val, bool *done);
|
||||
|
||||
/*
|
||||
* If initialized with throwOnNonCallable = false, check whether
|
||||
* the value is iterable.
|
||||
*/
|
||||
bool valueIsIterable() const {
|
||||
return iterator;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
#endif /* jsapi_h */
|
||||
|
|
|
@ -125,11 +125,11 @@ JS_SetCompartmentPrincipals(JSCompartment *compartment, JSPrincipals *principals
|
|||
|
||||
/* Safe to call with input obj == nullptr. Returns non-nullptr iff obj != nullptr. */
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_ObjectToInnerObject(JSContext *cx, JSObject *obj);
|
||||
JS_ObjectToInnerObject(JSContext *cx, JS::HandleObject obj);
|
||||
|
||||
/* Requires obj != nullptr. */
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_ObjectToOuterObject(JSContext *cx, JSObject *obj);
|
||||
JS_ObjectToOuterObject(JSContext *cx, JS::HandleObject obj);
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent);
|
||||
|
@ -1020,23 +1020,23 @@ JS_NewFloat64Array(JSContext *cx, uint32_t nelements);
|
|||
*/
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt8ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewInt8ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint8ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewUint8ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint8ClampedArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewUint8ClampedArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt16ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewInt16ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint16ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewUint16ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt32ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewInt32ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint32ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewUint32ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewFloat32ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewFloat32ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewFloat64ArrayFromArray(JSContext *cx, JSObject *array);
|
||||
JS_NewFloat64ArrayFromArray(JSContext *cx, JS::HandleObject array);
|
||||
|
||||
/*
|
||||
* Create a new typed array using the given ArrayBuffer for storage. The
|
||||
|
@ -1045,31 +1045,31 @@ JS_NewFloat64ArrayFromArray(JSContext *cx, JSObject *array);
|
|||
*/
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt8ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewInt8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint8ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewUint8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint8ClampedArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewUint8ClampedArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt16ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewInt16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint16ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewUint16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewInt32ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewInt32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewUint32ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewUint32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewFloat32ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewFloat32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_NewFloat64ArrayWithBuffer(JSContext *cx, JSObject *arrayBuffer,
|
||||
JS_NewFloat64ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
|
||||
uint32_t byteOffset, int32_t length);
|
||||
|
||||
/*
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using JS::ForOfIterator;
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
|
@ -1269,8 +1270,9 @@ const Class StopIterationObject::class_ = {
|
|||
};
|
||||
|
||||
bool
|
||||
ForOfIterator::init(HandleValue iterable)
|
||||
ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior)
|
||||
{
|
||||
JSContext *cx = cx_;
|
||||
RootedObject iterableObj(cx, ToObject(cx, iterable));
|
||||
if (!iterableObj)
|
||||
return false;
|
||||
|
@ -1285,10 +1287,13 @@ ForOfIterator::init(HandleValue iterable)
|
|||
if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
|
||||
return false;
|
||||
|
||||
// Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
|
||||
// for this kind of error anyway, but it would throw an inscrutable
|
||||
// error message about |method| rather than this nice one about |obj|.
|
||||
// Throw if obj[@@iterator] isn't callable if we were asked to do so.
|
||||
// js::Invoke is about to check for this kind of error anyway, but it would
|
||||
// throw an inscrutable error message about |method| rather than this nice
|
||||
// one about |obj|.
|
||||
if (!callee.isObject() || !callee.toObject().isCallable()) {
|
||||
if (nonIterableBehavior == AllowNonIterable)
|
||||
return true;
|
||||
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
|
||||
if (!bytes)
|
||||
return false;
|
||||
|
@ -1313,6 +1318,7 @@ ForOfIterator::next(MutableHandleValue vp, bool *done)
|
|||
{
|
||||
JS_ASSERT(iterator);
|
||||
|
||||
JSContext *cx = cx_;
|
||||
RootedValue method(cx);
|
||||
if (!JSObject::getProperty(cx, iterator, iterator, cx->names().next, &method))
|
||||
return false;
|
||||
|
|
|
@ -218,40 +218,6 @@ js_ThrowStopIteration(JSContext *cx);
|
|||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Convenience class for imitating a JS level for-of loop. Typical usage:
|
||||
*
|
||||
* ForOfIterator it(cx, iterable);
|
||||
* while (it.next()) {
|
||||
* if (!DoStuff(cx, it.value()))
|
||||
* return false;
|
||||
* }
|
||||
* if (!it.close())
|
||||
* return false;
|
||||
*
|
||||
* The final it.close() check is needed in order to check for cases where
|
||||
* any of the iterator operations fail.
|
||||
*
|
||||
* it.close() may be skipped only if something in the body of the loop fails
|
||||
* and the failure is allowed to propagate on cx, as in this example if DoStuff
|
||||
* fails. In that case, ForOfIterator's destructor does all necessary cleanup.
|
||||
*/
|
||||
class ForOfIterator
|
||||
{
|
||||
private:
|
||||
JSContext *cx;
|
||||
RootedObject iterator;
|
||||
|
||||
ForOfIterator(const ForOfIterator &) MOZ_DELETE;
|
||||
ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE;
|
||||
|
||||
public:
|
||||
ForOfIterator(JSContext *cx) : cx(cx), iterator(cx) { }
|
||||
|
||||
bool init(HandleValue iterable);
|
||||
bool next(MutableHandleValue val, bool *done);
|
||||
};
|
||||
|
||||
/*
|
||||
* Create an object of the form { value: VALUE, done: DONE }.
|
||||
* ES6 draft from 2013-09-05, section 25.4.3.4.
|
||||
|
|
|
@ -89,9 +89,8 @@ const Class JSObject::class_ = {
|
|||
const Class* const js::ObjectClassPtr = &JSObject::class_;
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
JS_ObjectToInnerObject(JSContext *cx, JSObject *objArg)
|
||||
JS_ObjectToInnerObject(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE);
|
||||
return nullptr;
|
||||
|
@ -100,9 +99,8 @@ JS_ObjectToInnerObject(JSContext *cx, JSObject *objArg)
|
|||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
JS_ObjectToOuterObject(JSContext *cx, JSObject *obj_)
|
||||
JS_ObjectToOuterObject(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
Rooted<JSObject*> obj(cx, obj_);
|
||||
assertSameCompartment(cx, obj);
|
||||
return GetOuterObject(cx, obj);
|
||||
}
|
||||
|
|
|
@ -224,10 +224,11 @@ GetPM(JSContext* cx, JS::HandleValue value, const char* fname)
|
|||
namespace JS {
|
||||
|
||||
JSObject*
|
||||
RegisterPerfMeasurement(JSContext *cx, JSObject *global)
|
||||
RegisterPerfMeasurement(JSContext *cx, HandleObject globalArg)
|
||||
{
|
||||
RootedObject global(cx, globalArg);
|
||||
RootedObject prototype(cx);
|
||||
prototype = JS_InitClass(cx, global, nullptr /* parent */,
|
||||
prototype = JS_InitClass(cx, global, js::NullPtr() /* parent */,
|
||||
&pm_class, pm_construct, 1,
|
||||
pm_props, pm_fns, 0, 0);
|
||||
if (!prototype)
|
||||
|
|
|
@ -118,7 +118,7 @@ class JS_FRIEND_API(PerfMeasurement)
|
|||
* global object). The JS-visible API is identical to the C++ API.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
RegisterPerfMeasurement(JSContext *cx, JSObject *global);
|
||||
RegisterPerfMeasurement(JSContext *cx, JS::HandleObject global);
|
||||
|
||||
/*
|
||||
* Given a Value which contains an instance of the aforementioned
|
||||
|
|
|
@ -5388,8 +5388,8 @@ NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options)
|
|||
};
|
||||
SetDOMCallbacks(cx->runtime(), &DOMcallbacks);
|
||||
|
||||
RootedObject domProto(cx, JS_InitClass(cx, glob, nullptr, &dom_class, dom_constructor, 0,
|
||||
dom_props, dom_methods, nullptr, nullptr));
|
||||
RootedObject domProto(cx, JS_InitClass(cx, glob, js::NullPtr(), &dom_class, dom_constructor,
|
||||
0, dom_props, dom_methods, nullptr, nullptr));
|
||||
if (!domProto)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ BreakpointSite::hasBreakpoint(Breakpoint *bp)
|
|||
Breakpoint::Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler)
|
||||
: debugger(debugger), site(site), handler(handler)
|
||||
{
|
||||
JS_ASSERT(handler->compartment() == debugger->object->compartment());
|
||||
JS_APPEND_LINK(&debuggerLinks, &debugger->breakpoints);
|
||||
JS_APPEND_LINK(&siteLinks, &site->breakpoints);
|
||||
}
|
||||
|
@ -2304,6 +2305,15 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
|||
else
|
||||
debuggees.remove(global);
|
||||
|
||||
/* Remove all breakpoints for the debuggee. */
|
||||
Breakpoint *nextbp;
|
||||
for (Breakpoint *bp = firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInDebugger();
|
||||
if (bp->site->script->compartment() == global->compartment())
|
||||
bp->destroy(fop);
|
||||
}
|
||||
JS_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
|
||||
|
||||
/*
|
||||
* The debuggee needs to be removed from the compartment last, as this can
|
||||
* trigger GCs if the compartment's debug mode is being changed, and the
|
||||
|
|
|
@ -59,6 +59,7 @@ using namespace js::types;
|
|||
using mozilla::DebugOnly;
|
||||
using mozilla::DoubleEqualsInt32;
|
||||
using mozilla::PodCopy;
|
||||
using JS::ForOfIterator;
|
||||
|
||||
/*
|
||||
* Note: when Clang 3.2 (32-bit) inlines the two functions below in Interpret,
|
||||
|
|
|
@ -3529,29 +3529,26 @@ const JSFunctionSpec _typedArray##Object::jsfuncs[] = {
|
|||
}
|
||||
#endif
|
||||
|
||||
#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \
|
||||
{ \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, JSObject *other_)\
|
||||
{ \
|
||||
Rooted<JSObject*> other(cx, other_); \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \
|
||||
JSObject *arrayBuffer_, uint32_t byteOffset, int32_t length) \
|
||||
{ \
|
||||
Rooted<JSObject*> arrayBuffer(cx, arrayBuffer_); \
|
||||
Rooted<JSObject*> proto(cx, nullptr); \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \
|
||||
length, proto); \
|
||||
} \
|
||||
JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \
|
||||
{ \
|
||||
if (!(obj = CheckedUnwrap(obj))) \
|
||||
return false; \
|
||||
const Class *clasp = obj->getClass(); \
|
||||
#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \
|
||||
{ \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \
|
||||
{ \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \
|
||||
HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \
|
||||
{ \
|
||||
return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \
|
||||
length, js::NullPtr()); \
|
||||
} \
|
||||
JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \
|
||||
{ \
|
||||
if (!(obj = CheckedUnwrap(obj))) \
|
||||
return false; \
|
||||
const Class *clasp = obj->getClass(); \
|
||||
return (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]); \
|
||||
}
|
||||
|
||||
|
|
|
@ -1271,8 +1271,7 @@ mozJSComponentLoader::ImportInto(const nsACString &aLocation,
|
|||
PromiseFlatCString(aLocation).get());
|
||||
}
|
||||
|
||||
if (!symbols.isObject() ||
|
||||
!JS_IsArrayObject(mContext, &symbols.toObject())) {
|
||||
if (!JS_IsArrayObject(mContext, symbols)) {
|
||||
return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
|
||||
PromiseFlatCString(aLocation).get());
|
||||
}
|
||||
|
|
|
@ -355,7 +355,10 @@ XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned
|
|||
// here, potentially an outer window proxy, and then an XPCWN.
|
||||
MOZ_ASSERT(js::IsWrapper(obj));
|
||||
RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false));
|
||||
MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, js::Wrapper::wrappedObject(obj)));
|
||||
#ifdef DEBUG
|
||||
JS::Rooted<JSObject*> wrappedObj(mJSContext, js::Wrapper::wrappedObject(obj));
|
||||
MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, wrappedObj));
|
||||
#endif
|
||||
|
||||
// Make sure we have an XPCWN, and grab it.
|
||||
if (!IS_WN_REFLECTOR(unwrapped))
|
||||
|
|
|
@ -32,7 +32,7 @@ XPCVariant::XPCVariant(JSContext* cx, jsval aJSVal)
|
|||
: mJSVal(aJSVal), mCCGeneration(0)
|
||||
{
|
||||
nsVariant::Initialize(&mData);
|
||||
if (!JSVAL_IS_PRIMITIVE(mJSVal)) {
|
||||
if (!mJSVal.isPrimitive()) {
|
||||
// XXXbholley - The innerization here was from bug 638026. Blake says
|
||||
// the basic problem was that we were storing the C++ inner but the JS
|
||||
// outer, which meant that, after navigation, the JS inner could be
|
||||
|
@ -42,8 +42,9 @@ XPCVariant::XPCVariant(JSContext* cx, jsval aJSVal)
|
|||
// thing, but I'm saving the cleanup here for another day. Blake thinks
|
||||
// that we should just not store the WN if we're creating a variant for
|
||||
// an outer window.
|
||||
JSObject *obj = JS_ObjectToInnerObject(cx, JSVAL_TO_OBJECT(mJSVal));
|
||||
mJSVal = OBJECT_TO_JSVAL(obj);
|
||||
JS::RootedObject obj(cx, &mJSVal.toObject());
|
||||
obj = JS_ObjectToInnerObject(cx, obj);
|
||||
mJSVal = JS::ObjectValue(*obj);
|
||||
|
||||
JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
|
||||
mReturnRawObject = !(unwrapped && IS_WN_REFLECTOR(unwrapped));
|
||||
|
|
|
@ -35,6 +35,7 @@ support-files =
|
|||
[test_bug717878_input_scroll.html]
|
||||
[test_bug869314.html]
|
||||
[test_bug903715.html]
|
||||
[test_bug935876.html]
|
||||
[test_bug957562.html]
|
||||
[test_bug960277.html]
|
||||
[test_listcontrol_search.html]
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=935876
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 935876</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=935876">Mozilla Bug 935876</a>
|
||||
<p id="display"></p>
|
||||
<div>
|
||||
<select id="listbox" size="3">
|
||||
<option selected>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
<option>7</option>
|
||||
</select>
|
||||
<select id="multipleListbox" size="3" multiple>
|
||||
<option selected>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
<option>7</option>
|
||||
</select>
|
||||
<select id="combobox">
|
||||
<option selected>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
<option>7</option>
|
||||
</select>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const kIsWin = navigator.platform.indexOf("Win") == 0;
|
||||
const kIsMac = navigator.platform.indexOf("Mac") == 0;
|
||||
const kIsAndroid = navigator.appVersion.indexOf("Android") != 0;
|
||||
|
||||
function runTests()
|
||||
{
|
||||
var doPreventDefault = false;
|
||||
function onKeydown(aEvent)
|
||||
{
|
||||
if (doPreventDefault) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
var keyPressEventFired = false;
|
||||
function onKeypress(aEvent)
|
||||
{
|
||||
keyPressEventFired = true;
|
||||
}
|
||||
|
||||
var keyDownEventConsumedByJS = false;
|
||||
var keyDownEventConsumed = false;
|
||||
function onkeydownInSystemEventGroup(aEvent)
|
||||
{
|
||||
keyDownEventConsumedByJS = aEvent.defaultPrevented;
|
||||
keyDownEventConsumed = aEvent.getPreventDefault();
|
||||
}
|
||||
|
||||
function reset()
|
||||
{
|
||||
keyPressEventFired = false;
|
||||
keyDownEventConsumedByJS = false;
|
||||
keyDownEventConsumed = false;
|
||||
}
|
||||
|
||||
function check(aExpectingKeydownConsumed, aDescription)
|
||||
{
|
||||
if (doPreventDefault) {
|
||||
ok(!keyPressEventFired, "keypress event shouldn't be fired for " + aDescription +
|
||||
" if preventDefault() of keydown event was called");
|
||||
ok(keyDownEventConsumedByJS, "keydown event of " + aDescription +
|
||||
" should be consumed in content level if preventDefault() of keydown event is called");
|
||||
ok(keyDownEventConsumed, "keydown event of " + aDescription +
|
||||
" should be consumed in system level if preventDefault() of keydown event is called");
|
||||
} else if (aExpectingKeydownConsumed) {
|
||||
ok(!keyPressEventFired, "keypress event shouldn't be fired for " + aDescription);
|
||||
ok(!keyDownEventConsumedByJS, "keydown event of " + aDescription + " shouldn't be consumed in content level");
|
||||
ok(keyDownEventConsumed, "keydown event of " + aDescription + " should be consumed in system level");
|
||||
} else {
|
||||
ok(keyPressEventFired, "keypress event should be fired for " + aDescription);
|
||||
ok(!keyDownEventConsumedByJS, "keydown event of " + aDescription + " shouldn't be consumed in content level");
|
||||
ok(!keyDownEventConsumed, "keydown event of " + aDescription + " should be consumed in system level");
|
||||
}
|
||||
}
|
||||
|
||||
var listbox = document.getElementById("listbox");
|
||||
listbox.addEventListener("keydown", onKeydown, false);
|
||||
listbox.addEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.addSystemEventListener(listbox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
|
||||
listbox.focus();
|
||||
|
||||
[ false, true ].forEach(function (consume) {
|
||||
doPreventDefault = consume;
|
||||
for (var i = 0; i < listbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_DOWN", {});
|
||||
check(true, "DownArrow key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < listbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_UP", {});
|
||||
check(true, "UpArrow key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < listbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_RIGHT", {});
|
||||
check(true, "RightArrow key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < listbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
check(true, "LeftArrow key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_DOWN", {});
|
||||
check(true, "PageDown key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_UP", {});
|
||||
check(true, "PageUp key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_END", {});
|
||||
check(true, "End key on listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_HOME", {});
|
||||
check(true, "Home key on listbox #" + i);
|
||||
}
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
check(false, "Enter key on listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
check(false, "Esc key on listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_F4", {});
|
||||
check(false, "F4 key on listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("a", {});
|
||||
check(false, "'A' key on listbox");
|
||||
});
|
||||
|
||||
listbox.removeEventListener("keydown", onKeydown, false);
|
||||
listbox.removeEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.removeSystemEventListener(listbox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
|
||||
|
||||
|
||||
var multipleListbox = document.getElementById("multipleListbox");
|
||||
multipleListbox.addEventListener("keydown", onKeydown, false);
|
||||
multipleListbox.addEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.addSystemEventListener(multipleListbox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
|
||||
multipleListbox.focus();
|
||||
|
||||
[ false, true ].forEach(function (consume) {
|
||||
doPreventDefault = consume;
|
||||
for (var i = 0; i < multipleListbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_DOWN", {});
|
||||
check(true, "DownArrow key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < multipleListbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_UP", {});
|
||||
check(true, "UpArrow key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < multipleListbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_RIGHT", {});
|
||||
check(true, "RightArrow key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < multipleListbox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
check(true, "LeftArrow key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_DOWN", {});
|
||||
check(true, "PageDown key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_UP", {});
|
||||
check(true, "PageUp key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_END", {});
|
||||
check(true, "End key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_HOME", {});
|
||||
check(true, "Home key on multiple listbox #" + i);
|
||||
}
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
check(true, "Enter key on multiple listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
check(false, "Esc key on multiple listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_F4", {});
|
||||
check(false, "F4 key on multiple listbox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("a", {});
|
||||
check(false, "'A' key on multiple listbox");
|
||||
});
|
||||
|
||||
multipleListbox.removeEventListener("keydown", onKeydown, false);
|
||||
multipleListbox.removeEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.removeSystemEventListener(multipleListbox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
|
||||
|
||||
|
||||
var combobox = document.getElementById("combobox");
|
||||
combobox.addEventListener("keydown", onKeydown, false);
|
||||
combobox.addEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.addSystemEventListener(combobox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
|
||||
combobox.focus();
|
||||
|
||||
[ false, true ].forEach(function (consume) {
|
||||
doPreventDefault = consume;
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_DOWN", {});
|
||||
check(true, "DownArrow key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_UP", {});
|
||||
check(true, "UpArrow key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_RIGHT", {});
|
||||
check(true, "RightArrow key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
check(true, "LeftArrow key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_DOWN", {});
|
||||
check(true, "PageDown key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_UP", {});
|
||||
check(true, "PageUp key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_END", {});
|
||||
check(true, "End key on combobox #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_HOME", {});
|
||||
check(true, "Home key on combobox #" + i);
|
||||
}
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
check(true, "Enter key on combobox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
check(true, "Esc key on combobox");
|
||||
|
||||
if (!kIsWin) {
|
||||
reset()
|
||||
synthesizeKey("VK_F4", {});
|
||||
check(false, "F4 key on combobox");
|
||||
}
|
||||
|
||||
reset()
|
||||
synthesizeKey("a", {});
|
||||
check(false, "'A' key on combobox");
|
||||
});
|
||||
|
||||
function finish()
|
||||
{
|
||||
combobox.removeEventListener("keydown", onKeydown, false);
|
||||
combobox.removeEventListener("keypress", onKeypress, false);
|
||||
SpecialPowers.removeSystemEventListener(combobox, "keydown", onkeydownInSystemEventGroup, false);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Mac uses native popup for dropdown. Let's skip the tests for popup
|
||||
// since it's not handled in nsListControlFrame.
|
||||
// Similarly, Android doesn't use popup for dropdown.
|
||||
if (kIsMac || kIsAndroid) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
function testDropDown(aCallback)
|
||||
{
|
||||
testOpenDropDown(function () {
|
||||
reset()
|
||||
synthesizeKey("VK_DOWN", { altKey: true });
|
||||
}, function () {
|
||||
check(true, "Alt + DownArrow key on combobox at opening dropdown");
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_DOWN", {});
|
||||
check(true, "DownArrow key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_UP", {});
|
||||
check(true, "UpArrow key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_RIGHT", {});
|
||||
check(true, "RightArrow key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < combobox.options.length + 1; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
check(true, "LeftArrow key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_DOWN", {});
|
||||
check(true, "PageDown key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_PAGE_UP", {});
|
||||
check(true, "PageUp key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_END", {});
|
||||
check(true, "End key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
reset()
|
||||
synthesizeKey("VK_HOME", {});
|
||||
check(true, "Home key on combobox during dropdown open #" + i);
|
||||
}
|
||||
|
||||
testCloseDropDown(function () {
|
||||
reset()
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
}, function () {
|
||||
testOpenDropDown(function () {
|
||||
check(true, "Enter key on combobox at closing dropdown");
|
||||
|
||||
synthesizeKey("VK_UP", { altKey: true });
|
||||
}, function () {
|
||||
check(true, "Alt + UpArrow key on combobox at opening dropdown");
|
||||
|
||||
testCloseDropDown(function () {
|
||||
reset()
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
}, function () {
|
||||
check(true, "Esc key on combobox at closing dropdown");
|
||||
|
||||
// F4 key opens/closes dropdown only on Windows. So, other platforms
|
||||
// don't need to do anymore.
|
||||
if (!kIsWin) {
|
||||
aCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
testOpenDropDown(function () {
|
||||
reset()
|
||||
synthesizeKey("VK_F4", {});
|
||||
}, function () {
|
||||
check(true, "F4 key on combobox at opening dropdown on Windows");
|
||||
|
||||
testCloseDropDown(function () {
|
||||
reset()
|
||||
synthesizeKey("VK_F4", {});
|
||||
}, function () {
|
||||
check(true, "F4 key on combobox at closing dropdown on Windows");
|
||||
|
||||
aCallback();
|
||||
return;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
doPreventDefault = false;
|
||||
testDropDown(function () {
|
||||
// Even if keydown event is consumed by JS, opening/closing dropdown
|
||||
// should work for a11y and security (e.g., cannot close dropdown causes
|
||||
// staying top-most window on the screen). If it's blocked by JS, this
|
||||
// test would cause permanent timeout.
|
||||
doPreventDefault = true;
|
||||
testDropDown(finish);
|
||||
});
|
||||
}
|
||||
|
||||
function testOpenDropDown(aTest, aOnOpenDropDown)
|
||||
{
|
||||
document.addEventListener("popupshowing", function (aEvent) {
|
||||
document.removeEventListener(aEvent.type, arguments.callee, false);
|
||||
setTimeout(aOnOpenDropDown, 0);
|
||||
}, false);
|
||||
aTest();
|
||||
}
|
||||
|
||||
function testCloseDropDown(aTest, aOnCloseDropDown)
|
||||
{
|
||||
document.addEventListener("popuphiding", function (aEvent) {
|
||||
document.removeEventListener(aEvent.type, arguments.callee, false);
|
||||
setTimeout(aOnCloseDropDown, 0)
|
||||
}, false);
|
||||
aTest();
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(runTests);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -390,6 +390,7 @@
|
|||
"layout/forms/test/test_bug571352.html":"shift-click multi-select not working?",
|
||||
"layout/forms/test/test_textarea_resize.html":"resizing textarea not available in b2g",
|
||||
"layout/forms/test/test_bug903715.html":"select elements don't use an in-page popup in B2G",
|
||||
"layout/forms/test/test_bug935876.html":"Esc key is consumed by another event handler only on desktop B2G",
|
||||
"layout/forms/test/test_listcontrol_search.html" : "select elements don't use an in-page popup in B2G",
|
||||
"layout/generic/test/test_bug392746.html":"ctrl mouse select not working in b2g",
|
||||
"layout/generic/test/test_bug470212.html":"shift mouse select not working in b2g",
|
||||
|
|
|
@ -413,14 +413,14 @@ GetIntFromJSObject(JSContext* aCtx,
|
|||
* The JSObject to get the object from.
|
||||
* @param aIndex
|
||||
* The index to get the object from.
|
||||
* @param _object
|
||||
* The JSObject pointer on success.
|
||||
* @param objOut
|
||||
* Set to the JSObject pointer on success.
|
||||
*/
|
||||
nsresult
|
||||
GetJSObjectFromArray(JSContext* aCtx,
|
||||
JSObject* aArray,
|
||||
JS::Handle<JSObject*> aArray,
|
||||
uint32_t aIndex,
|
||||
JSObject** _rooter)
|
||||
JS::MutableHandle<JSObject*> objOut)
|
||||
{
|
||||
NS_PRECONDITION(JS_IsArrayObject(aCtx, aArray),
|
||||
"Must provide an object that is an array!");
|
||||
|
@ -428,8 +428,8 @@ GetJSObjectFromArray(JSContext* aCtx,
|
|||
JS::Rooted<JS::Value> value(aCtx);
|
||||
bool rc = JS_GetElement(aCtx, aArray, aIndex, &value);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(value));
|
||||
*_rooter = JSVAL_TO_OBJECT(value);
|
||||
NS_ENSURE_ARG(!value.isPrimitive());
|
||||
objOut.set(&value.toObject());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2760,7 +2760,7 @@ History::UpdatePlaces(JS::Handle<JS::Value> aPlaceInfos,
|
|||
nsTArray<VisitData> visitData;
|
||||
for (uint32_t i = 0; i < infosLength; i++) {
|
||||
JS::Rooted<JSObject*> info(aCtx);
|
||||
nsresult rv = GetJSObjectFromArray(aCtx, infos, i, info.address());
|
||||
nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri = GetURIFromJSObject(aCtx, info, "uri");
|
||||
|
@ -2814,7 +2814,7 @@ History::UpdatePlaces(JS::Handle<JS::Value> aPlaceInfos,
|
|||
visitData.SetCapacity(visitData.Length() + visitsLength);
|
||||
for (uint32_t j = 0; j < visitsLength; j++) {
|
||||
JS::Rooted<JSObject*> visit(aCtx);
|
||||
rv = GetJSObjectFromArray(aCtx, visits, j, visit.address());
|
||||
rv = GetJSObjectFromArray(aCtx, visits, j, &visit);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
VisitData& data = *visitData.AppendElement(VisitData(uri));
|
||||
|
|
Загрузка…
Ссылка в новой задаче