Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2014-01-29 10:39:41 +00:00
commit 92aa154f50
53 changed files with 2167 additions and 519 deletions

View File

@ -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();

View File

@ -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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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,19 +507,26 @@ 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;
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
mUVSOffset,
symbolFont))) {
rv = NS_OK;
} else {
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;
@ -521,6 +539,7 @@ gfxDWriteFontEntry::ReadCMAP()
} else {
rv = NS_ERROR_NOT_AVAILABLE;
}
}
mHasCmapTable = NS_SUCCEEDED(rv);
if (mHasCmapTable) {
@ -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();
}

View File

@ -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

View File

@ -463,7 +463,7 @@ FT2FontEntry::CairoFontFace()
}
nsresult
FT2FontEntry::ReadCMAP()
FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
{
if (mCharacterMap) {
return NS_OK;

View File

@ -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;

View File

@ -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;
ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
otherFamilyNames, useFullName);
uint32_t n = otherFamilyNames.Length();
for (uint32_t i = 0; i < n; i++) {
aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
}
const gfxFontUtils::NameRecord *nameRecord =
reinterpret_cast<const gfxFontUtils::NameRecord*>(nameData + sizeof(gfxFontUtils::NameHeader));
uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
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;
}
}
}
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

View File

@ -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) {

View File

@ -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;
}
}
}

View File

@ -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 */

View File

@ -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,
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,32 +1333,31 @@ 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));
= reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset);
uint32_t i;
@ -1376,7 +1386,7 @@ gfxFontUtils::ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
uint32_t nameoff = nameRecord->offset; // offset from base of string storage
if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen)
> nameTableLen) {
> aDataLen) {
NS_WARNING("invalid font (name table strings)");
return NS_ERROR_FAILURE;
}
@ -1384,7 +1394,7 @@ gfxFontUtils::ReadNames(hb_blob_t *aNameTable, uint32_t aNameID,
// -- 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);
@ -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;
}
}
}

View File

@ -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,7 +826,7 @@ 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
@ -838,6 +835,10 @@ public:
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 */

View File

@ -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;
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);
}
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();
}

View File

@ -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;

View File

@ -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
};

View File

@ -222,31 +222,39 @@ 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 (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
mUVSOffset,
symbolFont))) {
rv = NS_OK;
} else {
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 char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
rv = gfxFontUtils::ReadCMAP((const uint8_t*)cmapData, 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()) {
// We assume a Graphite font knows what it's doing,
@ -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();
}

View File

@ -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];
// 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);
gfxFontFamily *familyEntry;
GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], 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

View File

@ -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();

View File

@ -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',

View File

@ -293,18 +293,18 @@ 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;

View File

@ -27,6 +27,7 @@ using mozilla::IsNaN;
using mozilla::Move;
using mozilla::ArrayLength;
using JS::DoubleNaNValue;
using JS::ForOfIterator;
/*** OrderedHashTable ****************************************************************************/

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);\
}");

View File

@ -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; }");

View File

@ -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);

View File

@ -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));

View File

@ -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);
}

View File

@ -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

View File

@ -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)
{

View File

@ -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 */

View File

@ -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);
/*

View File

@ -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;

View File

@ -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.

View File

@ -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);
}

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -3534,18 +3534,15 @@ const JSFunctionSpec _typedArray##Object::jsfuncs[] = {
{ \
return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
} \
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, JSObject *other_)\
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject 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) \
HandleObject 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); \
length, js::NullPtr()); \
} \
JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \
{ \

View File

@ -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());
}

View File

@ -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))

View File

@ -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));

View File

@ -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]

View File

@ -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>

View File

@ -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",

View File

@ -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));