Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-07-03 19:04:41 -04:00
Родитель e1307ad204 9dea96508b
Коммит 8471850074
74 изменённых файлов: 2965 добавлений и 453 удалений

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

@ -79,15 +79,18 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
// =================== Debugger ====================
SettingsListener.observe('devtools.debugger.enabled', false, function(enabled) {
Services.prefs.setBoolPref('devtools.debugger.enabled', value);
SettingsListener.observe('devtools.debugger.remote-enabled', false, function(enabled) {
Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
});
SettingsListener.observe('devtools.debugger.log', false, function(value) {
Services.prefs.setBoolPref('devtools.debugger.log', value);
});
SettingsListener.observe('devtools.debugger.port', 6000, function(value) {
Services.prefs.setIntPref('devtools.debugger.port', value);
SettingsListener.observe('devtools.debugger.remote-port', 6000, function(value) {
Services.prefs.setIntPref('devtools.debugger.remote-port', value);
});
SettingsListener.observe('devtools.debugger.force-local', true, function(value) {
Services.prefs.setBoolPref('devtools.debugger.force-local', value);
});

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

@ -5261,10 +5261,9 @@ function middleMousePaste(event) {
Cu.reportError(ex);
}
// FIXME: Bug 631500, use openUILink directly
let where = whereToOpenLink(event, true);
openUILinkIn(url, where,
{ disallowInheritPrincipal: !mayInheritPrincipal.value });
openUILink(url, event,
{ ignoreButton: true,
disallowInheritPrincipal: !mayInheritPrincipal.value });
event.stopPropagation();
}
@ -6914,6 +6913,7 @@ let gPrivateBrowsingUI = {
_searchBarValue: null,
_findBarValue: null,
_inited: false,
_initCallbacks: [],
init: function PBUI_init() {
Services.obs.addObserver(this, "private-browsing", false);
@ -6926,6 +6926,9 @@ let gPrivateBrowsingUI = {
this.onEnterPrivateBrowsing(true);
this._inited = true;
this._initCallbacks.forEach(function (callback) callback.apply());
this._initCallbacks = [];
},
uninit: function PBUI_unint() {
@ -6936,6 +6939,17 @@ let gPrivateBrowsingUI = {
Services.obs.removeObserver(this, "private-browsing-transition-complete");
},
get initialized() {
return this._inited;
},
addInitializationCallback: function PBUI_addInitializationCallback(aCallback) {
if (this._inited)
return;
this._initCallbacks.push(aCallback);
},
get _disableUIOnToggle() {
if (this._privateBrowsingService.autoStarted)
return false;
@ -7138,14 +7152,16 @@ let gPrivateBrowsingUI = {
!this.privateBrowsingEnabled;
},
get autoStarted() {
return this._privateBrowsingService.autoStarted;
},
get privateBrowsingEnabled() {
return this._privateBrowsingService.privateBrowsingEnabled;
},
/**
* These accessors are used to support per-window Private Browsing mode.
* For now the getter returns nsIPrivateBrowsingService.privateBrowsingEnabled,
* and the setter should only be used in tests.
* This accessor is used to support per-window Private Browsing mode.
*/
get privateWindow() {
if (!gBrowser)

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

@ -12,10 +12,16 @@ XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
const TOPIC = "private-browsing-transition-complete";
function getNewTabPageURL() {
if (("gPrivateBrowsingUI" in window) && gPrivateBrowsingUI.privateWindow)
return "about:privatebrowsing";
else
return Services.prefs.getCharPref(PREF) || "about:blank";
if (("gPrivateBrowsingUI" in window)) {
// gPrivateBrowsingUI may not be initialized yet, in that case we'll
// update BROWSER_NEW_TAB_URL when it gets initialized.
if (!gPrivateBrowsingUI.initialized)
gPrivateBrowsingUI.addInitializationCallback(update);
else if (gPrivateBrowsingUI.privateWindow &&
!gPrivateBrowsingUI.autoStarted)
return "about:privatebrowsing";
}
return Services.prefs.getCharPref(PREF) || "about:blank";
}
function update() {

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

@ -132,14 +132,6 @@ leaktest.py: leaktest.py.in
chmod +x $@
GARBAGE += leaktest.py
ifneq (android,$(MOZ_WIDGET_TOOLKIT))
ifdef MOZ_UPDATER
update-settings.ini: update-settings.ini.in $(APP_INI_DEPS)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
GARBAGE += update-settings.ini
endif
endif
ifdef MOZ_APP_BASENAME
application.ini: application.ini.in $(APP_INI_DEPS)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@

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

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

@ -131,7 +131,6 @@ MOZ_WAVE = @MOZ_WAVE@
MOZ_MEDIA = @MOZ_MEDIA@
MOZ_VORBIS = @MOZ_VORBIS@
MOZ_TREMOR = @MOZ_TREMOR@
MOZ_NO_THEORA_ASM = @MOZ_NO_THEORA_ASM@
MOZ_OPUS = @MOZ_OPUS@
MOZ_WEBM = @MOZ_WEBM@
MOZ_MEDIA_PLUGINS = @MOZ_MEDIA_PLUGINS@

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

@ -63,6 +63,7 @@ def _nsinstall_internal(argv):
# just create one directory?
def maybe_create_dir(dir, mode, try_again):
dir = os.path.abspath(dir)
if os.path.exists(dir):
if not os.path.isdir(dir):
print >> sys.stderr, ('nsinstall: %s is not a directory' % dir)

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

@ -1026,42 +1026,10 @@ fi
fi # COMPILE_ENVIRONMENT
dnl clang prior to 2.9 (including Xcode 4) does not support all the
dnl constructs required by the libtheora inline asm. This is used to
dnl detect and disable it
AC_MSG_CHECKING([whether the C compiler is clang older than version 2.9])
AC_LANG_SAVE
AC_LANG_C
AC_TRY_COMPILE([
#if defined(__clang__)
# if __clang_major__<2 || (__clang_major__==2 && __clang_minor__<9)
# error "clang older than 2.9 detected"
# endif
#endif
],
[],
result="no",
result="yes")
AC_LANG_RESTORE
AC_MSG_RESULT([$result])
if test "$result" = "yes"; then
MOZ_NO_THEORA_ASM=1
fi
dnl XCode 4.2 also shipped an llvm-gcc which couldn't compile
dnl the libtheora inline asm.
AC_MSG_CHECKING([bad gcc versions])
if `$CC --version | grep -q "Apple Inc. build 5658"`; then
AC_MSG_RESULT([Apple build 5658])
MOZ_NO_THEORA_ASM=1
else
AC_MSG_RESULT([we're ok])
fi
AC_SUBST(MOZ_NO_THEORA_ASM)
if test "x$MOZ_NO_THEORA_ASM" = x1; then
AC_MSG_WARN([Disabling libtheora inline assembly])
fi
AC_MSG_CHECKING([compiler version])
# Just print it so it shows up in the logs.
cc_version=$($CC --version)
AC_MSG_RESULT([$cc_version])
if test -n "$MAKE"; then
if test `echo $MAKE | grep -c make.py` != 1; then

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

@ -10678,14 +10678,9 @@ nsGlobalWindow::SetIsApp(bool aValue)
bool
nsGlobalWindow::IsPartOfApp()
{
mozIDOMApplication* app;
nsresult rv = GetApp(&app);
nsCOMPtr<mozIDOMApplication> app;
if (NS_FAILED(rv)) {
return false;
}
return app != nsnull;
return NS_SUCCEEDED(GetApp(getter_AddRefs(app))) ? app != nsnull : false;
}
nsresult

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

@ -2,7 +2,7 @@
* 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/. */
"use strict"
"use strict";
/* static functions */
let DEBUG = 0;
@ -78,6 +78,29 @@ ContactAddress.prototype = {
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactAddress])
}
//ContactEmail
const CONTACTEMAIL_CONTRACTID = "@mozilla.org/contactEmail;1";
const CONTACTEMAIL_CID = Components.ID("{94811520-c11f-11e1-afa7-0800200c9a66}");
const nsIDOMContactEmail = Components.interfaces.nsIDOMContactEmail;
function ContactEmail(aType, aAddress) {
this.type = aType || null;
this.address = aAddress || null;
};
ContactEmail.prototype = {
classID : CONTACTEMAIL_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTEMAIL_CID,
contractID: CONTACTEMAIL_CONTRACTID,
classDescription: "ContactEmail",
interfaces: [nsIDOMContactEmail],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactEmail])
}
//ContactTelephone
const CONTACTTELEPHONE_CONTRACTID = "@mozilla.org/contactTelephone;1";
@ -133,10 +156,16 @@ Contact.prototype = {
init: function init(aProp) {
// Accept non-array strings for DOMString[] properties and convert them.
function _create(aField) {
if (typeof aField == "string")
return new Array(aField);
return aField;
function _create(aField) {
if (Array.isArray(aField)) {
for (let i = 0; i < aField.length; i++) {
if (typeof aField[i] !== "string")
aField[i] = String(aField[i]);
}
return aField;
} else if (aField != null) {
return [String(aField)];
}
};
this.name = _create(aProp.name) || null;
@ -146,7 +175,16 @@ Contact.prototype = {
this.familyName = _create(aProp.familyName) || null;
this.honorificSuffix = _create(aProp.honorificSuffix) || null;
this.nickname = _create(aProp.nickname) || null;
this.email = _create(aProp.email) || null;
if (aProp.email) {
aProp.email = Array.isArray(aProp.email) ? aProp.email : [aProp.email];
this.email = new Array();
for (let i = 0; i < aProp.email.length; i++)
this.email.push(new ContactEmail(aProp.email[i].type, aProp.email[i].address));
} else {
this.email = null;
}
this.photo = _create(aProp.photo) || null;
this.url = _create(aProp.url) || null;
this.category = _create(aProp.category) || null;
@ -445,4 +483,4 @@ ContactManager.prototype = {
}
const NSGetFactory = XPCOMUtils.generateNSGetFactory(
[Contact, ContactManager, ContactProperties, ContactAddress, ContactTelephone, ContactFindOptions])
[Contact, ContactManager, ContactProperties, ContactAddress, ContactTelephone, ContactFindOptions, ContactEmail])

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

@ -7,6 +7,9 @@ contract @mozilla.org/contactAddress;1 {eba48030-89e8-11e1-b0c4-0800200c9a66}
component {82601b20-89e8-11e1-b0c4-0800200c9a66} ContactManager.js
contract @mozilla.org/contactTelephone;1 {82601b20-89e8-11e1-b0c4-0800200c9a66}
component {94811520-c11f-11e1-afa7-0800200c9a66} ContactManager.js
contract @mozilla.org/contactEmail;1 {94811520-c11f-11e1-afa7-0800200c9a66}
component {e31daea0-0cb6-11e1-be50-0800200c9a66} ContactManager.js
contract @mozilla.org/contactFindOptions;1 {e31daea0-0cb6-11e1-be50-0800200c9a66}

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

@ -22,7 +22,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
const DB_NAME = "contacts";
const DB_VERSION = 2;
const DB_VERSION = 3;
const STORE_NAME = "contacts";
function ContactDB(aGlobal) {
@ -101,6 +101,31 @@ ContactDB.prototype = {
// Create new searchable indexes.
objectStore.createIndex("tel", "search.tel", { unique: false, multiEntry: true });
objectStore.createIndex("category", "properties.category", { unique: false, multiEntry: true });
} else if (currVersion == 2) {
debug("upgrade 2");
// Create a new scheme for the email field. We move from an array of emailaddresses to an array of
// ContactEmail.
if (!objectStore) {
objectStore = aTransaction.objectStore(STORE_NAME);
}
// Delete old email index.
objectStore.deleteIndex("email");
// Upgrade existing email field in the DB.
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
debug("upgrade email1: " + JSON.stringify(cursor.value));
cursor.value.properties.email =
cursor.value.properties.email.map(function(address) { return { address: address }; });
cursor.update(cursor.value);
debug("upgrade email2: " + JSON.stringify(cursor.value));
cursor.continue();
}
};
// Create new searchable indexes.
objectStore.createIndex("email", "search.email", { unique: false, multiEntry: true });
}
}
},
@ -172,8 +197,16 @@ ContactDB.prototype = {
}
}
debug("lookup: " + JSON.stringify(contact.search[field]));
} else if (field == "email") {
let address = aContact.properties[field][i].address;
if (address && typeof address == "string") {
contact.search[field].push(address.toLowerCase());
}
} else {
contact.search[field].push(aContact.properties[field][i].toLowerCase());
let val = aContact.properties[field][i];
if (typeof val == "string") {
contact.search[field].push(val.toLowerCase());
}
}
}
}
@ -319,7 +352,9 @@ ContactDB.prototype = {
request = index.mozGetAll(options.filterValue, limit);
} else {
// not case sensitive
let tmp = options.filterValue.toLowerCase();
let tmp = typeof options.filterValue == "string"
? options.filterValue.toLowerCase()
: options.filterValue.toString().toLowerCase();
let range = this._global.IDBKeyRange.bound(tmp, tmp + "\uFFFF");
let index = store.index(key + "LowerCase");
request = index.mozGetAll(range, limit);

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

@ -2,7 +2,7 @@
* 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/. */
"use strict"
"use strict";
let DEBUG = 0;
if (DEBUG)

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

@ -76,7 +76,8 @@ var properties1 = {
givenName: ["Test1","Test2"],
nickname: "nicktest",
tel: [{type: "work", number: "123456"} , {type: "home", number: "+9-876-5432"}],
adr: adr1
adr: adr1,
email: [{type: "work", address: "x@y.com"}]
};
var properties2 = {
@ -88,7 +89,7 @@ var properties2 = {
additionalName: "dummyadditionalName",
nickname: "dummyNickname",
tel: [{type: "test", number: "123456789"},{type: "home", number: "234567890"}],
email: ["a@b.c", "b@c.d"],
email: [{type: "test", address: "a@b.c"}, {address: "b@c.d"}],
adr: [adr1, adr2],
impp: ["im1", "im2"],
org: ["org1", "org2"],
@ -148,6 +149,11 @@ function checkTel(tel1, tel2) {
checkStr(tel1.number, tel2.number, "Same number");
}
function checkEmail(email1, email2) {
checkStr(email1.type, email2.type, "Same type");
checkStr(email1.address, email2.address, "Same address");
}
function checkContacts(contact1, contact2) {
checkStr(contact1.name, contact2.name, "Same name");
checkStr(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
@ -156,7 +162,6 @@ function checkContacts(contact1, contact2) {
checkStr(contact1.familyName, contact2.familyName, "Same familyName");
checkStr(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
checkStr(contact1.nickname, contact2.nickname, "Same nickname");
checkStr(contact1.email, contact2.email, "Same email");
checkStr(contact1.photo, contact2.photo, "Same photo");
checkStr(contact1.url, contact2.url, "Same url");
checkStr(contact1.category, contact2.category, "Same category");
@ -169,6 +174,8 @@ function checkContacts(contact1, contact2) {
is(contact1.sex, contact2.sex, "Same sex");
is(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
for (var i in contact1.email)
checkEmail(contact1.email[i], contact2.email[i]);
for (var i in contact1.adr)
checkAddress(contact1.adr[i], contact2.adr[i]);
for (var i in contact1.tel)
@ -269,6 +276,21 @@ var steps = [
};
req.onerror = onFailure;
},
function () {
ok(true, "Searching for exact email");
var options = {filterBy: ["email"],
filterOp: "equals",
filterValue: properties1.email[0].address};
req = mozContacts.find(options);
req.onsuccess = function () {
ok(req.result.length == 1, "Found exactly 1 contact.");
findResult1 = req.result[0];
ok(findResult1.id == sample_id1, "Same ID");
checkContacts(findResult1, createResult1);
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Retrieving by substring and update");
mozContacts.oncontactchange = function(event) {
@ -616,14 +638,14 @@ var steps = [
},
function () {
ok(true, "Modifying contact3");
findResult1.email = (properties1.nickname);
findResult1.email = [{address: properties1.nickname}];
findResult1.nickname = "TEST";
var newContact = new mozContact();
newContact.init(findResult1);
req = mozContacts.save(newContact);
req.onsuccess = function () {
var options = {filterBy: ["nickname", "email", "name"],
filterOp: "equals",
filterOp: "contains",
filterValue: properties1.nickname};
// One contact has it in nickname and the other in email
var req2 = mozContacts.find(options);
@ -741,7 +763,7 @@ var steps = [
ok(true, "Searching contacts by email");
var options = {filterBy: ["email"],
filterOp: "contains",
filterValue: properties2.email[0].substring(0, 4)};
filterValue: properties2.email[0].address.substring(0, 4)};
req = mozContacts.find(options);
req.onsuccess = function () {
ok(req.result.length == 1, "Found exactly 1 contact.");
@ -865,12 +887,12 @@ var steps = [
ok(true, "Testing clone contact2");
var cloned = new mozContact(createResult1);
ok(cloned.id != createResult1.id, "Cloned contact has new ID");
cloned.email = "new email!";
cloned.email = {address: "new email!"};
cloned.givenName = "Tom";
req = mozContacts.save(cloned);
req.onsuccess = function () {
ok(cloned.id, "The contact now has an ID.");
ok(cloned.email == "new email!", "Same Email");
ok(cloned.email.address == "new email!", "Same Email");
ok(createResult1.email != cloned.email, "Clone has different email");
ok(cloned.givenName == "Tom", "New Name");
next();
@ -892,7 +914,7 @@ var steps = [
function () {
ok(true, "Search with redundant fields should only return 1 contact");
createResult1 = new mozContact();
createResult1.init({name: "XXX", nickname: "XXX", email: "XXX", tel: {number: "XXX"}});
createResult1.init({name: "XXX", nickname: "XXX", email: [{address: "XXX"}], tel: {number: "XXX"}});
req = mozContacts.save(createResult1);
req.onsuccess = function() {
var options = {filterBy: [],
@ -1098,6 +1120,40 @@ var steps = [
}
req.onerror = onFailure;
},
function () {
ok(true, "Adding empty contact");
createResult1 = new mozContact();
createResult1.init({givenName: 5});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
sample_id1 = createResult1.id;
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Test category search with equals");
var options = {filterBy: ["givenName"],
filterOp: "contains",
filterValue: 5};
req = mozContacts.find(options);
req.onsuccess = function () {
ok(req.result.length == 1, "1 Entry.");
checkContacts(req.result[0], createResult1);
next();
}
req.onerror = onFailure;
},
function () {
ok(true, "Deleting database");
req = mozContacts.clear()
req.onsuccess = function () {
ok(true, "Deleted the database");
next();
}
req.onerror = onFailure;
},
function () {
ok(true, "all done!\n");
clearTemps();

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

@ -25,6 +25,13 @@ interface nsIDOMContactTelephone : nsISupports
attribute DOMString number;
};
[scriptable, uuid(94811520-c11f-11e1-afa7-0800200c9a66)]
interface nsIDOMContactEmail : nsISupports
{
attribute DOMString type;
attribute DOMString address;
};
[scriptable, uuid(e31daea0-0cb6-11e1-be50-0800200c9a66)]
interface nsIDOMContactFindOptions : nsISupports
{
@ -46,7 +53,7 @@ interface nsIDOMContactProperties : nsISupports
attribute jsval familyName; // DOMString[]
attribute jsval honorificSuffix; // DOMString[]
attribute jsval nickname; // DOMString[]
attribute jsval email; // DOMString[]
attribute jsval email; // ContactEmail[]
attribute jsval photo; // DOMString[]
attribute jsval url; // DOMString[]
attribute jsval category; // DOMString[]

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

@ -519,6 +519,7 @@ var interfaceNamesInGlobalScope =
"WebGLActiveInfo",
"SVGGradientElement",
"ContactTelephone",
"ContactEmail",
"SVGFitToViewBox",
"SVGAElement"
]

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

@ -3979,8 +3979,9 @@ gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript)
gfxPlatformFontList::PlatformFontList()->
SystemFindFontForChar(aCh, aRunScript, &mStyle);
if (fe) {
// ignore bolder considerations in system fallback case...
nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false);
bool wantBold = mStyle.ComputeWeight() >= 6;
nsRefPtr<gfxFont> font =
fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
return font.forget();
}

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

@ -643,6 +643,17 @@ public:
// if so set the mIsSimpleFamily flag (defaults to False before we've checked)
void CheckForSimpleFamily();
// check whether the family has any faces that are marked as Italic
bool HasItalicFace() const {
size_t count = mAvailableFonts.Length();
for (size_t i = 0; i < count; ++i) {
if (mAvailableFonts[i] && mAvailableFonts[i]->IsItalic()) {
return true;
}
}
return false;
}
// For memory reporter
virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
FontListSizes* aSizes) const;

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

@ -274,11 +274,30 @@ gfxGDIFont::Initialize()
LOGFONTW logFont;
// Figure out if we want to do synthetic oblique styling.
GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
bool wantFakeItalic =
(mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) &&
!fe->IsItalic();
// If the font's family has an actual italic face (but font matching
// didn't choose it), we have to use a cairo transform instead of asking
// GDI to italicize, because that would use a different face and result
// in a possible glyph ID mismatch between shaping and rendering.
//
// The font entry's mFamilyHasItalicFace flag is needed for user fonts
// where the *CSS* family may not know about italic faces that are present
// in the *GDI* family, and which GDI would use if we asked it to perform
// the "italicization".
bool useCairoFakeItalic = wantFakeItalic &&
(fe->Family()->HasItalicFace() || fe->mFamilyHasItalicFace);
if (mAdjustedSize == 0.0) {
mAdjustedSize = mStyle.size;
if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) {
// to implement font-size-adjust, we first create the "unadjusted" font
FillLogFont(logFont, mAdjustedSize);
FillLogFont(logFont, mAdjustedSize,
wantFakeItalic && !useCairoFakeItalic);
mFont = ::CreateFontIndirectW(&logFont);
// initialize its metrics so we can calculate size adjustment
@ -306,7 +325,7 @@ gfxGDIFont::Initialize()
// this may end up being zero
mAdjustedSize = ROUND(mAdjustedSize);
FillLogFont(logFont, mAdjustedSize);
FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
mFont = ::CreateFontIndirectW(&logFont);
mMetrics = new gfxFont::Metrics;
@ -351,7 +370,7 @@ gfxGDIFont::Initialize()
} else {
// Make a best-effort guess at extended metrics
// this is based on general typographic guidelines
// GetTextMetrics can fail if the font file has been removed
// or corrupted recently.
BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
@ -428,8 +447,9 @@ gfxGDIFont::Initialize()
cairo_matrix_init_identity(&ctm);
cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
bool italic = (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
if (italic && !mFontEntry->IsItalic()) {
if (useCairoFakeItalic) {
// Skew the matrix to do fake italic if it wasn't already applied
// via the LOGFONT
double skewfactor = OBLIQUE_SKEW_FACTOR;
cairo_matrix_t style;
cairo_matrix_init(&style,
@ -479,7 +499,8 @@ gfxGDIFont::Initialize()
}
void
gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize)
gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
bool aUseGDIFakeItalic)
{
GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
@ -501,6 +522,11 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize)
fe->FillLogFont(&aLogFont, weight, aSize,
(mAntialiasOption == kAntialiasSubpixel) ? true : false);
// If GDI synthetic italic is wanted, force the lfItalic field to true
if (aUseGDIFakeItalic) {
aLogFont.lfItalic = 1;
}
}
PRInt32

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

@ -71,7 +71,10 @@ protected:
void Initialize(); // creates metrics and Cairo fonts
void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize);
// Fill the given LOGFONT record according to our style, but don't adjust
// the lfItalic field if we're going to use a cairo transform for fake
// italics.
void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
// mPlatformShaper is used for the GDI shaper, mUniscribeShaper
// for the Uniscribe version if needed

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

@ -161,11 +161,13 @@ FontTypeToOutPrecision(PRUint8 fontType)
GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
gfxWindowsFontType aFontType,
bool aItalic, PRUint16 aWeight, PRInt16 aStretch,
gfxUserFontData *aUserFontData)
gfxUserFontData *aUserFontData,
bool aFamilyHasItalicFace)
: gfxFontEntry(aFaceName),
mWindowsFamily(0), mWindowsPitch(0),
mFontType(aFontType),
mForceGDI(false),
mFamilyHasItalicFace(aFamilyHasItalicFace),
mCharset(), mUnicodeRanges()
{
mUserFontData = aUserFontData;
@ -403,7 +405,7 @@ GDIFontEntry::InitLogFont(const nsAString& aName,
gfxWindowsFontType aFontType)
{
#define CLIP_TURNOFF_FONTASSOCIATION 0x40
mLogFont.lfHeight = -1;
// Fill in logFont structure
@ -433,12 +435,14 @@ GDIFontEntry*
GDIFontEntry::CreateFontEntry(const nsAString& aName,
gfxWindowsFontType aFontType, bool aItalic,
PRUint16 aWeight, PRInt16 aStretch,
gfxUserFontData* aUserFontData)
gfxUserFontData* aUserFontData,
bool aFamilyHasItalicFace)
{
// jtdfix - need to set charset, unicode ranges, pitch/family
GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic,
aWeight, aStretch, aUserFontData);
aWeight, aStretch, aUserFontData,
aFamilyHasItalicFace);
return fe;
}
@ -495,10 +499,14 @@ GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
}
}
// We can't set the hasItalicFace flag correctly here,
// because we might not have seen the family's italic face(s) yet.
// Later code does _not_ rely on this flag for platform fonts;
// it is only needed for fonts loaded with src:local
fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName),
feType, (logFont.lfItalic == 0xFF),
(PRUint16) (logFont.lfWeight), 0,
nsnull);
nsnull, false);
if (!fe)
return 1;
@ -758,7 +766,8 @@ gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(),
gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
lookup->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
lookup->mWeight, aProxyEntry->mStretch, nsnull);
lookup->mWeight, aProxyEntry->mStretch, nsnull,
lookup->Family()->HasItalicFace());
if (!fe)
return nsnull;
@ -980,7 +989,7 @@ gfxGDIFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName,
gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
PRUint32(aProxyEntry->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL),
w, aProxyEntry->mStretch, winUserFontData);
w, aProxyEntry->mStretch, winUserFontData, false);
if (!fe)
return fe;

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

@ -247,7 +247,8 @@ public:
gfxWindowsFontType aFontType,
bool aItalic,
PRUint16 aWeight, PRInt16 aStretch,
gfxUserFontData* aUserFontData);
gfxUserFontData* aUserFontData,
bool aFamilyHasItalicFace);
// create a font entry for a font referenced by its fullname
static GDIFontEntry* LoadLocalFont(const gfxProxyFontEntry &aProxyEntry,
@ -259,6 +260,12 @@ public:
gfxWindowsFontType mFontType;
bool mForceGDI : 1;
// For src:local user-fonts, we keep track of whether the platform family
// contains an italic face, because in this case we can't safely ask GDI
// to create synthetic italics (oblique) via the LOGFONT.
// (For other types of font, this is just set to false.)
bool mFamilyHasItalicFace : 1;
gfxSparseBitSet mCharset;
gfxSparseBitSet mUnicodeRanges;
@ -267,7 +274,7 @@ protected:
GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
bool aItalic, PRUint16 aWeight, PRInt16 aStretch,
gfxUserFontData *aUserFontData);
gfxUserFontData *aUserFontData, bool aFamilyHasItalicFace);
void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);

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

@ -63,6 +63,7 @@ def _nsinstall_internal(argv):
# just create one directory?
def maybe_create_dir(dir, mode, try_again):
dir = os.path.abspath(dir)
if os.path.exists(dir):
if not os.path.isdir(dir):
print >> sys.stderr, ('nsinstall: %s is not a directory' % dir)

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

@ -3229,7 +3229,7 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
* don't resolve name accesses on lambdas in DeclEnv objects on the
* scope chain.
*/
if (atom == CallObjectLambdaName(script->function()))
if (atom == CallObjectLambdaName(*script->function()))
return access;
if (!script->nesting()->parent)
@ -5022,7 +5022,7 @@ TypeScript::SetScope(JSContext *cx, JSScript *script_, JSObject *scope_)
* object has been created in the heavyweight case.
*/
JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
scope->asCall().getCalleeFunction() != fun);
&scope->asCall().callee() != fun);
if (!script->compileAndGo) {
script->types->global = NULL;
@ -5069,7 +5069,7 @@ TypeScript::SetScope(JSContext *cx, JSScript *script_, JSObject *scope_)
JS_ASSERT(!call.isForEval());
/* Don't track non-heavyweight parents, NAME ops won't reach into them. */
JSFunction *parentFun = call.getCalleeFunction();
JSFunction *parentFun = &call.callee();
if (!parentFun || !parentFun->isHeavyweight())
return true;
JSScript *parent = parentFun->script();
@ -5188,7 +5188,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
JSScript *parent = script->nesting()->parent;
JS_ASSERT(parent);
while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
while (!scope->isCall() || scope->asCall().callee().script() != parent)
scope = &scope->asScope().enclosingScope();
if (scope != parent->nesting()->activeCall) {

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

@ -540,9 +540,9 @@ namespace js {
* scope chain above call objects for fun.
*/
static inline JSAtom *
CallObjectLambdaName(JSFunction *fun)
CallObjectLambdaName(JSFunction &fun)
{
return (fun->flags & JSFUN_LAMBDA) ? fun->atom.get() : NULL;
return (fun.flags & JSFUN_LAMBDA) ? fun.atom.get() : NULL;
}
} /* namespace js */

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

@ -235,9 +235,6 @@ class Bindings
} /* namespace js */
#define JS_OBJECT_ARRAY_SIZE(length) \
(offsetof(ObjectArray, vector) + sizeof(JSObject *) * (length))
#ifdef JS_METHODJIT
namespace JSC {
class ExecutablePool;

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

@ -349,10 +349,10 @@ class SetPropCompiler : public PICStubCompiler
// \\ V and getters, and
// \===/ 2. arguments and locals have different getters
// then we can rely on fun->nargs remaining invariant.
JSFunction *fun = obj->asCall().getCalleeFunction();
JSFunction &fun = obj->asCall().callee();
uint16_t slot = uint16_t(shape->shortid());
if (shape->setterOp() == CallObject::setVarOp)
slot += fun->nargs;
slot += fun.nargs;
slot += CallObject::RESERVED_SLOTS;
Address address = masm.objPropAddress(obj, pic.objReg, slot);
masm.storeValue(pic.u.vr, address);
@ -603,8 +603,8 @@ class SetPropCompiler : public PICStubCompiler
* objects may differ due to eval(), DEFFUN, etc.).
*/
RecompilationMonitor monitor(cx);
JSFunction *fun = obj->asCall().getCalleeFunction();
JSScript *script = fun->script();
JSFunction &fun = obj->asCall().callee();
JSScript *script = fun.script();
uint16_t slot = uint16_t(shape->shortid());
if (!script->ensureHasTypes(cx))
return error();
@ -1583,10 +1583,10 @@ class ScopeNameCompiler : public PICStubCompiler
Jump finalShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
ImmPtr(getprop.holder->lastProperty()));
JSFunction *fun = getprop.holder->asCall().getCalleeFunction();
JSFunction &fun = getprop.holder->asCall().callee();
unsigned slot = shape->shortid();
if (kind == VAR)
slot += fun->nargs;
slot += fun.nargs;
slot += CallObject::RESERVED_SLOTS;
Address address = masm.objPropAddress(obj, pic.objReg, slot);

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

@ -4354,11 +4354,11 @@ DebuggerEnv_getCallee(JSContext *cx, unsigned argc, Value *vp)
if (!scope.isCall())
return true;
JSObject *callee = scope.asCall().getCallee();
if (!callee)
CallObject &callobj = scope.asCall();
if (callobj.isForEval())
return true;
args.rval() = ObjectValue(*callee);
args.rval() = ObjectValue(callobj.callee());
if (!dbg->wrapDebuggeeValue(cx, &args.rval()))
return false;
return true;

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

@ -65,70 +65,57 @@ CallObject::isForEval() const
return getReservedSlot(CALLEE_SLOT).isNull();
}
inline void
CallObject::setCallee(JSObject *callee)
inline JSFunction &
CallObject::callee() const
{
JS_ASSERT_IF(callee, callee->isFunction());
setFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
}
inline JSObject *
CallObject::getCallee() const
{
return getReservedSlot(CALLEE_SLOT).toObjectOrNull();
}
inline JSFunction *
CallObject::getCalleeFunction() const
{
return getReservedSlot(CALLEE_SLOT).toObject().toFunction();
return *getReservedSlot(CALLEE_SLOT).toObject().toFunction();
}
inline const Value &
CallObject::arg(unsigned i, MaybeCheckAliasing checkAliasing) const
{
JS_ASSERT_IF(checkAliasing, getCalleeFunction()->script()->formalLivesInCallObject(i));
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
return getSlot(RESERVED_SLOTS + i);
}
inline void
CallObject::setArg(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
{
JS_ASSERT_IF(checkAliasing, getCalleeFunction()->script()->formalLivesInCallObject(i));
JS_ASSERT_IF(checkAliasing, callee().script()->formalLivesInCallObject(i));
setSlot(RESERVED_SLOTS + i, v);
}
inline const Value &
CallObject::var(unsigned i, MaybeCheckAliasing checkAliasing) const
{
JSFunction *fun = getCalleeFunction();
JS_ASSERT_IF(checkAliasing, fun->script()->varIsAliased(i));
return getSlot(RESERVED_SLOTS + fun->nargs + i);
JSFunction &fun = callee();
JS_ASSERT_IF(checkAliasing, fun.script()->varIsAliased(i));
return getSlot(RESERVED_SLOTS + fun.nargs + i);
}
inline void
CallObject::setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
{
JSFunction *fun = getCalleeFunction();
JS_ASSERT_IF(checkAliasing, fun->script()->varIsAliased(i));
setSlot(RESERVED_SLOTS + fun->nargs + i, v);
JSFunction &fun = callee();
JS_ASSERT_IF(checkAliasing, fun.script()->varIsAliased(i));
setSlot(RESERVED_SLOTS + fun.nargs + i, v);
}
inline HeapSlotArray
CallObject::argArray()
{
DebugOnly<JSFunction*> fun = getCalleeFunction();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun->nargs));
JSFunction &fun = callee();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun.nargs));
return HeapSlotArray(getSlotAddress(RESERVED_SLOTS));
}
inline HeapSlotArray
CallObject::varArray()
{
JSFunction *fun = getCalleeFunction();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun->nargs,
fun->script()->bindings.numVars()));
return HeapSlotArray(getSlotAddress(RESERVED_SLOTS + fun->nargs));
JSFunction &fun = callee();
JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun.nargs,
fun.script()->bindings.numVars()));
return HeapSlotArray(getSlotAddress(RESERVED_SLOTS + fun.nargs));
}
inline uint32_t

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

@ -184,7 +184,7 @@ CallObject::createForFunction(JSContext *cx, StackFrame *fp)
void
CallObject::copyUnaliasedValues(StackFrame *fp)
{
JS_ASSERT(fp->script() == getCalleeFunction()->script());
JS_ASSERT(fp->script() == callee().script());
JSScript *script = fp->script();
/* If bindings are accessed dynamically, everything is aliased. */
@ -227,7 +227,7 @@ CallObject::setArgOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
unsigned i = (uint16_t) JSID_TO_INT(id);
JSScript *script = callobj.getCalleeFunction()->script();
JSScript *script = callobj.callee().script();
JS_ASSERT(script->formalLivesInCallObject(i));
callobj.setArg(i, *vp);
@ -247,7 +247,7 @@ CallObject::setVarOp(JSContext *cx, HandleObject obj, HandleId id, JSBool strict
JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
unsigned i = (uint16_t) JSID_TO_INT(id);
JSScript *script = callobj.getCalleeFunction()->script();
JSScript *script = callobj.callee().script();
JS_ASSERT(script->varIsAliased(i));
callobj.setVar(i, *vp);
@ -984,7 +984,7 @@ ScopeIter::operator++()
case Call:
if (hasScopeObject_) {
cur_ = &cur_->asCall().enclosingScope();
if (CallObjectLambdaName(fp_->fun()))
if (CallObjectLambdaName(*fp_->fun()))
cur_ = &cur_->asDeclEnv().enclosingScope();
}
fp_ = NULL;
@ -1071,7 +1071,7 @@ ScopeIter::settle()
CallObject &callobj = cur_->asCall();
type_ = callobj.isForEval() ? StrictEvalScope : Call;
hasScopeObject_ = true;
JS_ASSERT_IF(type_ == Call, callobj.getCalleeFunction()->script() == fp_->script());
JS_ASSERT_IF(type_ == Call, callobj.callee().script() == fp_->script());
} else {
JS_ASSERT(!cur_->isScope());
JS_ASSERT(fp_->isGlobalFrame() || fp_->isDebuggerFrame());
@ -1142,7 +1142,7 @@ class DebugScopeProxy : public BaseProxyHandler
if (scope.isCall() && !scope.asCall().isForEval()) {
CallObject &callobj = scope.asCall();
JSScript *script = callobj.getCalleeFunction()->script();
JSScript *script = callobj.callee().script();
if (!script->ensureHasTypes(cx))
return false;
@ -1249,7 +1249,7 @@ class DebugScopeProxy : public BaseProxyHandler
static bool isMissingArgumentsBinding(ScopeObject &scope)
{
return isFunctionScope(scope) &&
!scope.asCall().getCalleeFunction()->script()->argumentsHasVarBinding();
!scope.asCall().callee().script()->argumentsHasVarBinding();
}
/*
@ -1266,7 +1266,7 @@ class DebugScopeProxy : public BaseProxyHandler
if (!isArguments(cx, id) || !isFunctionScope(scope))
return true;
JSScript *script = scope.asCall().getCalleeFunction()->script();
JSScript *script = scope.asCall().callee().script();
if (script->needsArgsObj())
return true;
@ -1788,7 +1788,7 @@ GetDebugScopeForScope(JSContext *cx, ScopeObject &scope, const ScopeIter &enclos
JSObject &maybeDecl = scope.enclosingScope();
if (maybeDecl.isDeclEnv()) {
JS_ASSERT(CallObjectLambdaName(scope.asCall().getCalleeFunction()));
JS_ASSERT(CallObjectLambdaName(scope.asCall().callee()));
enclosingDebug = DebugScopeObject::create(cx, maybeDecl.asDeclEnv(), enclosingDebug);
if (!enclosingDebug)
return NULL;
@ -1830,7 +1830,7 @@ GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
return NULL;
if (callobj->enclosingScope().isDeclEnv()) {
JS_ASSERT(CallObjectLambdaName(callobj->getCalleeFunction()));
JS_ASSERT(CallObjectLambdaName(callobj->callee()));
DeclEnvObject &declenv = callobj->enclosingScope().asDeclEnv();
enclosingDebug = DebugScopeObject::create(cx, declenv, enclosingDebug);
if (!enclosingDebug)

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

@ -135,16 +135,14 @@ class CallObject : public ScopeObject
static CallObject *createForFunction(JSContext *cx, StackFrame *fp);
static CallObject *createForStrictEval(JSContext *cx, StackFrame *fp);
/* True if this is for a strict mode eval frame or for a function call. */
/* True if this is for a strict mode eval frame. */
inline bool isForEval() const;
/*
* The callee function if this CallObject was created for a function
* invocation, or null if it was created for a strict mode eval frame.
* Returns the function for which this CallObject was created. (This may
* only be called if !isForEval.)
*/
inline JSObject *getCallee() const;
inline JSFunction *getCalleeFunction() const;
inline void setCallee(JSObject *callee);
inline JSFunction &callee() const;
/* Returns the formal argument at the given index. */
inline const Value &arg(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;

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

@ -300,11 +300,10 @@ StackFrame::epilogue(JSContext *cx)
JS_ASSERT(isNonEvalFunctionFrame());
if (fun()->isHeavyweight()) {
JS_ASSERT_IF(hasCallObj(),
scopeChain()->asCall().getCalleeFunction()->script() == script());
JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().callee().script() == script());
} else {
JS_ASSERT(!scopeChain()->isCall() || scopeChain()->asCall().isForEval() ||
scopeChain()->asCall().getCalleeFunction()->script() != script());
scopeChain()->asCall().callee().script() != script());
}
if (cx->compartment->debugMode())

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

@ -64,7 +64,7 @@ public:
mRect = aTransform.Transform(mRect);
for (int i = 0; i < ArrayLength(mRadii); i += 2) {
for (size_t i = 0; i < ArrayLength(mRadii); i += 2) {
mRadii[i] *= aTransform.xx;
mRadii[i + 1] *= aTransform.yy;
}

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

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Test for bold when system fallback is used</title>
</head>
<body>
<!--
-- On OS X and Win7, the Plantagenet Cherokee font is only available in Regular weight.
--
-- Cherokee text including <b> elements taken from
-- http://chr.wikipedia.org/wiki/%E1%8F%A9%E1%8E%A6%E1%8F%A7%E1%8E%A7%E1%8F%85%E1%8F%8D%E1%8F%95%E1%8E%BE_%E1%8E%A4%E1%8F%82%E1%8E%BE%E1%8F%97%E1%8F%85%E1%8F%97
--
-->
<p>ᏩᎦᏧᎧᏅᏍᏕᎾ ᎤᏂᎾᏗᏅᏗ ᎨᏒᎢ ᎠᏂᏙᎾᎥ ᎠᎴᏫᏍᏙᏗ ᎢᏳᏩᎪᏗ ᎯᎳᎪ ᏚᏂᎬᏩᎶᏔᏅ ᎠᎴ ᎠᎵᏌᎳᏗᏍᎬ
ᎭᏫᎾᏗᏢ ᎠᏕᎸ ᏄᎾᏍᏛ ᎤᏂᎾᏗᏅᏗ ᎦᏣᏄᎳᎯᎨ ᎬᎾᏬᏍᎬ ᎤᎾᏤᎵ ᎧᏃᎮᏍᎩ ᏄᎵᏍᏔᏅ ᎢᎦᏘᎭ ᎢᎬᏁᎸ, ᎭᏫᎾᏗᏢ ᏧᏓᎴᎾᎢ
ᎠᎵᏐᏆᎸᏗ ᎤᏂᎾᏗᏅᏗ ᎦᏙ ᎤᏍᏗ ᎨᏒᎢ ᎠᏂᏙᎾᎥ ᎠᎴᏫᏍᏙᏗ ᎢᏳᏩᎪᏗ ᎯᎳᎪ ᏚᏂᎬᏩᎶᏔᏅ ᎠᎴ ᎦᎶᏍᎬ.</p>
</body>
</html>

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

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Test for bold when system fallback is used</title>
</head>
<body>
<!--
-- On OS X and Win7, the Plantagenet Cherokee font is only available in Regular weight.
--
-- Cherokee text including <b> elements taken from
-- http://chr.wikipedia.org/wiki/%E1%8F%A9%E1%8E%A6%E1%8F%A7%E1%8E%A7%E1%8F%85%E1%8F%8D%E1%8F%95%E1%8E%BE_%E1%8E%A4%E1%8F%82%E1%8E%BE%E1%8F%97%E1%8F%85%E1%8F%97
--
-->
<p><b>ᏩᎦᏧᎧᏅᏍᏕᎾ ᎤᏂᎾᏗᏅᏗ</b> ᎨᏒᎢ ᎠᏂᏙᎾᎥ ᎠᎴᏫᏍᏙᏗ ᎢᏳᏩᎪᏗ ᎯᎳᎪ ᏚᏂᎬᏩᎶᏔᏅ ᎠᎴ ᎠᎵᏌᎳᏗᏍᎬ
ᎭᏫᎾᏗᏢ ᎠᏕᎸ ᏄᎾᏍᏛ ᎤᏂᎾᏗᏅᏗ ᎦᏣᏄᎳᎯᎨ ᎬᎾᏬᏍᎬ ᎤᎾᏤᎵ ᎧᏃᎮᏍᎩ ᏄᎵᏍᏔᏅ ᎢᎦᏘᎭ ᎢᎬᏁᎸ, ᎭᏫᎾᏗᏢ ᏧᏓᎴᎾᎢ
<b>ᎠᎵᏐᏆᎸᏗ ᎤᏂᎾᏗᏅᏗ</b> ᎦᏙ ᎤᏍᏗ ᎨᏒᎢ ᎠᏂᏙᎾᎥ ᎠᎴᏫᏍᏙᏗ ᎢᏳᏩᎪᏗ ᎯᎳᎪ ᏚᏂᎬᏩᎶᏔᏅ ᎠᎴ ᎦᎶᏍᎬ.</p>
</body>
</html>

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

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Test for bold when system fallback is used</title>
</head>
<body>
<!--
-- Chess symbols will use Droid Sans Fallback on Android,
-- which only has a single weight.
--
-- On some platforms there may be "regular" glyphs in the Bold face
-- of the fallback font used (e.g. Menlo on OS X), so this will
-- not reliably pass there.
-->
<p>&#x2654;&#x2655;&#x2656;&#x2657;&#x2658;&#x2659;</p>
<p>&#x2654;&#x2655;&#x2656;&#x2657;&#x2658;&#x2659;</p>
</body>
</html>

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

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Test for bold when system fallback is used</title>
</head>
<body>
<!--
-- Chess symbols will use Droid Sans Fallback on Android,
-- which only has a single weight.
--
-- On some platforms there may be "regular" glyphs in the Bold face
-- of the fallback font used (e.g. Menlo on OS X), so this will
-- not reliably pass there.
-->
<p>&#x2654;&#x2655;&#x2656;&#x2657;&#x2658;&#x2659;</p>
<p><b>&#x2654;&#x2655;&#x2656;&#x2657;&#x2658;&#x2659;</b></p>
</body>
</html>

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

@ -11,7 +11,7 @@
!= defaultfont-bolditalic.html defaultfont.html
!= defaultfont-bolditalic.html defaultfont-bold.html
fails-if(Android) != defaultjapanese-bold.html defaultjapanese.html
!= defaultjapanese-bold.html defaultjapanese.html
!= defaultjapanese-italic.html defaultjapanese.html
!= defaultjapanese-oblique.html defaultjapanese.html
!= defaultjapanese-bolditalic.html defaultjapanese.html
@ -75,3 +75,9 @@ HTTP(..) == font-stretch-1.html font-stretch-1-ref.html
!= synthetic-style-1.html synthetic-style-1-notref.html
!= synthetic-style-2.html synthetic-style-2-notref.html
# Bug 765906 - synthetic bold should be used if necessary together with system fallback.
# **NOTE** we skip these on Linux because of bug 769659.
# test 1 uses Cherokee; expected to pass on OS X and Win7
random-if(!(cocoaWidget||/^Windows\x20NT\x206\.1/.test(http.oscpu))) skip-if(gtk2Widget) != bold-system-fallback-1.html bold-system-fallback-1-notref.html
# test 2 uses Chess symbols; expected to pass on Android
random-if(!Android) skip-if(gtk2Widget) != bold-system-fallback-2.html bold-system-fallback-2-notref.html

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

@ -21,7 +21,6 @@ DEFINES += -DTHEORA_DISABLE_ENCODE
ifeq ($(findstring 86,$(OS_TEST)), 86)
ifneq ($(OS_ARCH),SunOS)
ifneq ($(OS_ARCH)$(OS_TEST),WINNTx86_64)
ifndef MOZ_NO_THEORA_ASM
DEFINES += -DOC_X86_ASM
ifeq (64,$(findstring 64,$(OS_TEST)))
DEFINES += -DOC_X86_64_ASM
@ -29,7 +28,6 @@ endif
endif
endif
endif
endif
VPATH := $(srcdir)
@ -63,7 +61,6 @@ CSRCS += \
$(NULL)
endif
else
ifndef MOZ_NO_THEORA_ASM
VPATH += $(srcdir)/x86
CSRCS += \
@ -76,7 +73,6 @@ CSRCS += \
$(NULL)
endif
endif
endif
ifdef GNU_AS
ifeq ($(findstring arm,$(OS_TEST)), arm)

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

@ -986,26 +986,27 @@ public class PanZoomController
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
// When zooming is enabled, wait to see if there's a double-tap.
if (mController.getAllowZoom())
return false;
sendPointToGecko("Gesture:SingleTap", motionEvent);
return true;
if (!mController.getAllowZoom()) {
sendPointToGecko("Gesture:SingleTap", motionEvent);
}
// return false because we still want to get the ACTION_UP event that triggers this
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
// When zooming is disabled, we handle this in onSingleTapUp.
if (!mController.getAllowZoom())
return false;
sendPointToGecko("Gesture:SingleTap", motionEvent);
if (mController.getAllowZoom()) {
sendPointToGecko("Gesture:SingleTap", motionEvent);
}
return true;
}
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
if (!mController.getAllowZoom())
return false;
sendPointToGecko("Gesture:DoubleTap", motionEvent);
if (mController.getAllowZoom()) {
sendPointToGecko("Gesture:DoubleTap", motionEvent);
}
return true;
}

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

@ -1746,8 +1746,11 @@ var SelectionHandler = {
win = win.parent;
}
return (aX - offset.x > this.cache.rect.left && aX - offset.x < this.cache.rect.right) &&
(aY - offset.y > this.cache.rect.top && aY - offset.y < this.cache.rect.bottom);
let radius = ElementTouchHelper.getTouchRadius();
return (aX - offset.x > this.cache.rect.left - radius.left &&
aX - offset.x < this.cache.rect.right + radius.right &&
aY - offset.y > this.cache.rect.top - radius.top &&
aY - offset.y < this.cache.rect.bottom + radius.bottom);
},
// Returns true if the selection has been reversed. Takes optional aIsStartHandle
@ -3623,6 +3626,19 @@ const ElementTouchHelper = {
return elem;
},
/* Returns the touch radius in content px. */
getTouchRadius: function getTouchRadius() {
let dpiRatio = ViewportHandler.displayDPI / kReferenceDpi;
let zoom = BrowserApp.selectedTab._zoom;
return {
top: this.radius.top * dpiRatio / zoom,
right: this.radius.right * dpiRatio / zoom,
bottom: this.radius.bottom * dpiRatio / zoom,
left: this.radius.left * dpiRatio / zoom
};
},
/* Returns the touch radius in reference pixels. */
get radius() {
let prefs = Services.prefs;
delete this.radius;
@ -3640,11 +3656,6 @@ const ElementTouchHelper = {
/* Retrieve the closest element to a point by looking at borders position */
getClosest: function getClosest(aWindowUtils, aX, aY) {
if (!this.dpiRatio)
this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
let dpiRatio = this.dpiRatio;
let target = aWindowUtils.elementFromPoint(aX, aY,
true, /* ignore root scroll frame*/
false); /* don't flush layout */
@ -3657,11 +3668,8 @@ const ElementTouchHelper = {
return target;
target = null;
let zoom = BrowserApp.selectedTab._zoom;
let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio / zoom,
this.radius.right * dpiRatio / zoom,
this.radius.bottom * dpiRatio / zoom,
this.radius.left * dpiRatio / zoom, true, false);
let radius = this.getTouchRadius();
let nodes = aWindowUtils.nodesFromRect(aX, aY, radius.top, radius.right, radius.bottom, radius.left, true, false);
let threshold = Number.POSITIVE_INFINITY;
for (let i = 0; i < nodes.length; i++) {

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

@ -1,14 +0,0 @@
/* 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/. */
@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
@namespace html url(http://www.w3.org/1999/xhtml);
xul|*:-moz-system-metric(touch-enabled) {
cursor: none !important;
}
html|*:-moz-system-metric(touch-enabled) {
cursor: none !important;
}

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

@ -29,8 +29,6 @@ chrome.jar:
content/bindings/content.xml (content/bindings/content.xml)
content/bindings/menulist.xml (content/bindings/menulist.xml)
content/bindings/settings.xml (content/bindings/settings.xml)
content/cursor.css (content/cursor.css)
% content branding %content/branding/
content/sanitize.js (content/sanitize.js)
content/exceptions.js (content/exceptions.js)
* content/downloads.js (content/downloads.js)
@ -39,6 +37,8 @@ chrome.jar:
content/HelperApps.js (content/HelperApps.js)
content/dbg-browser-actors.js (content/dbg-browser-actors.js)
% content branding %content/branding/
% override chrome://global/content/config.xul chrome://browser/content/config.xhtml
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
% override chrome://mozapps/content/extensions/extensions.xul chrome://browser/content/aboutAddons.xhtml

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

@ -33,7 +33,6 @@ category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1
# stylesheets
category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css
category agent-style-sheets browser-cursor-stylesheet chrome://browser/content/cursor.css
# ContentPermissionPrompt.js
component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} ContentPermissionPrompt.js

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

@ -5522,6 +5522,11 @@ nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
}
return NS_ERROR_DOCUMENT_NOT_CACHED;
}
if (mCanceled)
// If the request was canceled then don't continue without using
// the cache entry. See bug #764337
return rv;
// proceed without using the cache
}

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

@ -0,0 +1,583 @@
/* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/.
*/
/*
* This is an implementation of a "Shared Worker" using an iframe in the
* hidden DOM window. A subset of new APIs are introduced to the window
* by cloning methods from the worker's JS origin.
*/
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
const EXPORTED_SYMBOLS = ["FrameWorker"];
var workerCache = {}; // keyed by URL.
var _nextPortId = 1;
// Retrieves a reference to a WorkerHandle associated with a FrameWorker and a
// new ClientPort.
function FrameWorker(url, clientWindow, name) {
// first create the client port we are going to use. Laster we will
// message the worker to create the worker port.
let portid = _nextPortId++;
let clientPort = new ClientPort(portid, clientWindow);
let existingWorker = workerCache[url];
if (!existingWorker) {
// setup the worker and add this connection to the pending queue
let worker = new FrameWorkerObject(url, clientWindow, name);
worker.pendingPorts.push(clientPort);
existingWorker = workerCache[url] = worker;
} else {
// already have a worker - either queue or make the connection.
if (existingWorker.loaded) {
try {
clientPort._createWorkerAndEntangle(existingWorker);
}
catch (ex) {
Cu.reportError("FrameWorker: Failed to connect a port: " + e + "\n" + e.stack);
}
} else {
existingWorker.pendingPorts.push(clientPort);
}
}
// return the pseudo worker object.
return new WorkerHandle(clientPort, existingWorker);
};
/**
* FrameWorker
*
* A FrameWorker is an iframe that is attached to the hiddenWindow,
* which contains a pair of MessagePorts. It is constructed with the
* URL of some JavaScript that will be run in the context of the window;
* the script does not have a full DOM but is instead run in a sandbox
* that has a select set of methods cloned from the URL's domain.
*/
function FrameWorkerObject(url, name) {
this.url = url;
this.name = name || url;
this.ports = {};
this.pendingPorts = [];
this.loaded = false;
this.frame = makeHiddenFrame();
var self = this;
Services.obs.addObserver(function injectController(doc, topic, data) {
if (!doc.defaultView || doc.defaultView != self.frame.contentWindow) {
return;
}
Services.obs.removeObserver(injectController, "document-element-inserted", false);
try {
self.createSandbox();
} catch (e) {
Cu.reportError("FrameWorker: failed to create sandbox for " + url + ". " + e);
}
}, "document-element-inserted", false);
this.frame.setAttribute("src", url);
}
FrameWorkerObject.prototype = {
createSandbox: function createSandbox() {
let workerWindow = this.frame.contentWindow;
let sandbox = new Cu.Sandbox(workerWindow);
// copy the window apis onto the sandbox namespace only functions or
// objects that are naturally a part of an iframe, I'm assuming they are
// safe to import this way
let workerAPI = ['MozWebSocket', 'WebSocket', 'localStorage',
'atob', 'btoa', 'clearInterval', 'clearTimeout', 'dump',
'setInterval', 'setTimeout', 'XMLHttpRequest',
'MozBlobBuilder', 'FileReader', 'Blob',
'navigator', 'location'];
workerAPI.forEach(function(fn) {
try {
// XXX Need to unwrap for this to work - find out why!
sandbox[fn] = XPCNativeWrapper.unwrap(workerWindow)[fn];
}
catch(e) {
Cu.reportError("FrameWorker: failed to import API "+fn+"\n"+e+"\n");
}
});
// and we delegate ononline and onoffline events to the worker.
// See http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#workerglobalscope
this.frame.addEventListener('offline', function fw_onoffline(event) {
Cu.evalInSandbox("onoffline();", sandbox);
}, false);
this.frame.addEventListener('online', function fw_ononline(event) {
Cu.evalInSandbox("ononline();", sandbox);
}, false);
sandbox._postMessage = function fw_postMessage(d, o) {
workerWindow.postMessage(d, o)
};
sandbox._addEventListener = function fw_addEventListener(t, l, c) {
workerWindow.addEventListener(t, l, c)
};
// And a very hacky work-around for bug 734215
sandbox.bufferToArrayHack = function fw_bufferToArrayHack(a) {
return new workerWindow.Uint8Array(a);
};
this.sandbox = sandbox;
let worker = this;
workerWindow.addEventListener("load", function loadListener() {
// the iframe has loaded the js file as text - first inject the magic
// port-handling code into the sandbox.
function getProtoSource(ob) {
let raw = ob.prototype.toSource();
return ob.name + ".prototype=" + raw + ";"
}
try {
let scriptText = [importScripts.toSource(),
AbstractPort.toSource(),
getProtoSource(AbstractPort),
WorkerPort.toSource(),
getProtoSource(WorkerPort),
// *sigh* - toSource() doesn't do __proto__
"WorkerPort.prototype.__proto__=AbstractPort.prototype;",
__initWorkerMessageHandler.toSource(),
"__initWorkerMessageHandler();" // and bootstrap it.
].join("\n")
Cu.evalInSandbox(scriptText, sandbox, "1.8", "<injected port handling code>", 1);
}
catch (e) {
Cu.reportError("FrameWorker: Error injecting port code into content side of the worker: " + e + "\n" + e.stack);
}
// and wire up the client message handling.
try {
initClientMessageHandler(worker, workerWindow);
}
catch (e) {
Cu.reportError("FrameWorker: Error setting up event listener for chrome side of the worker: " + e + "\n" + e.stack);
}
// Now get the worker js code and eval it into the sandbox
try {
let scriptText = workerWindow.document.body.textContent;
Cu.evalInSandbox(scriptText, sandbox, "1.8", workerWindow.location.href, 1);
} catch (e) {
Cu.reportError("FrameWorker: Error evaluating worker script for " + worker.name + ": " + e + "; " +
(e.lineNumber ? ("Line #" + e.lineNumber) : "") +
(e.stack ? ("\n" + e.stack) : ""));
return;
}
// so finally we are ready to roll - dequeue all the pending connects
worker.loaded = true;
let pending = worker.pendingPorts;
while (pending.length) {
let port = pending.shift();
if (port._portid) { // may have already been closed!
try {
port._createWorkerAndEntangle(worker);
}
catch(e) {
Cu.reportError("FrameWorker: Failed to create worker port: " + e + "\n" + e.stack);
}
}
}
});
},
terminate: function terminate() {
// closing the port also removes it from this.ports via port-close
for (let [portid, port] in Iterator(this.ports)) {
// port may have been closed as a side-effect from closing another port
if (!port)
continue;
try {
port.close();
} catch (ex) {
Cu.reportError("FrameWorker: failed to close port. " + ex);
}
}
// let pending events get delivered before actually removing the frame
Services.tm.mainThread.dispatch(function deleteWorkerFrame() {
// now nuke the iframe itself and forget everything about this worker.
let doc = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService)
.hiddenDOMWindow.document;
doc.documentElement.removeChild(this.frame);
delete workerCache[this.url];
}.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
}
};
function makeHiddenFrame() {
let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
let iframe = hiddenDoc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
iframe.setAttribute("mozframetype", "content");
hiddenDoc.documentElement.appendChild(iframe);
// Disable some types of content
let docShell = iframe.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell);
docShell.allowAuth = false;
docShell.allowPlugins = false;
docShell.allowImages = false;
docShell.allowWindowControl = false;
// TODO: disable media (bug 759964)
// Mark this docShell as a "browserFrame", to break script access to e.g. window.top
docShell.isBrowserFrame = true;
return iframe;
}
function WorkerHandle(port, worker) {
this.port = port;
this._worker = worker;
}
WorkerHandle.prototype = {
__exposedProps__: {
port: "r",
terminate: "r"
},
// XXX - workers have no .close() method, but *do* have a .terminate()
// method which we should implement. However, the worker spec doesn't define
// a callback to be made in the worker when this happens - it all just dies.
// TODO: work out a sane impl for 'terminate'.
terminate: function terminate() {
this._worker.terminate();
}
};
// This function is magically injected into the sandbox and used there.
// Thus, it is only ever dealing with "worker" ports.
function __initWorkerMessageHandler() {
let ports = {}; // all "worker" ports currently alive, keyed by ID.
function messageHandler(event) {
// We will ignore all messages destined for otherType.
let data = event.data;
let portid = data.portId;
let port;
if (!data.portFromType || data.portFromType === "worker") {
// this is a message posted by ourself so ignore it.
return;
}
switch (data.portTopic) {
case "port-create":
// a new port was created on the "client" side - create a new worker
// port and store it in the map
port = new WorkerPort(portid);
ports[portid] = port;
// and call the "onconnect" handler.
onconnect({ports: [port]});
break;
case "port-close":
// the client side of the port was closed, so close this side too.
port = ports[portid];
if (!port) {
// port already closed (which will happen when we call port.close()
// below - the client side will send us this message but we've
// already closed it.)
return;
}
delete ports[portid];
port.close();
break;
case "port-message":
// the client posted a message to this worker port.
port = ports[portid];
if (!port) {
// port must be closed - this shouldn't happen!
return;
}
port._onmessage(data.data);
break;
default:
break;
}
}
// addEventListener is injected into the sandbox.
_addEventListener('message', messageHandler);
}
// And this is the message listener for the *client* (ie, chrome) side of the world.
function initClientMessageHandler(worker, workerWindow) {
function _messageHandler(event) {
// We will ignore all messages destined for otherType.
let data = event.data;
let portid = data.portId;
let port;
if (!data.portFromType || data.portFromType === "client") {
// this is a message posted by ourself so ignore it.
return;
}
switch (data.portTopic) {
// No "port-create" here - client ports are created explicitly.
case "port-close":
// the worker side of the port was closed, so close this side too.
port = worker.ports[portid];
if (!port) {
// port already closed (which will happen when we call port.close()
// below - the worker side will send us this message but we've
// already closed it.)
return;
}
delete worker.ports[portid];
port.close();
break;
case "port-message":
// the client posted a message to this worker port.
port = worker.ports[portid];
if (!port) {
return;
}
port._onmessage(data.data);
break;
default:
break;
}
}
// this can probably go once debugged and working correctly!
function messageHandler(event) {
try {
_messageHandler(event);
} catch (ex) {
Cu.reportError("FrameWorker: Error handling client port control message: " + ex + "\n" + ex.stack);
}
}
workerWindow.addEventListener('message', messageHandler);
}
// The port implementation which is shared between clients and workers.
function AbstractPort(portid) {
this._portid = portid;
this._handler = undefined;
// pending messages sent to this port before it has a message handler.
this._pendingMessagesIncoming = [];
}
AbstractPort.prototype = {
_portType: null, // set by a subclass.
// abstract methods to be overridden.
_dopost: function fw_AbstractPort_dopost(data) {
throw new Error("not implemented");
},
_onerror: function fw_AbstractPort_onerror(err) {
throw new Error("not implemented");
},
// and concrete methods shared by client and workers.
toString: function fw_AbstractPort_toString() {
return "MessagePort(portType='" + this._portType + "', portId=" + this._portid + ")";
},
_JSONParse: function fw_AbstractPort_JSONParse(data) JSON.parse(data),
_postControlMessage: function fw_AbstractPort_postControlMessage(topic, data) {
let postData = {portTopic: topic,
portId: this._portid,
portFromType: this._portType,
data: data,
__exposedProps__: {
portTopic: 'r',
portId: 'r',
portFromType: 'r',
data: 'r'
}
};
this._dopost(postData);
},
_onmessage: function fw_AbstractPort_onmessage(data) {
// See comments in postMessage below - we work around a cloning
// issue by using JSON for these messages.
// Further, we allow the workers to override exactly how the JSON parsing
// is done - we try and do such parsing in the client window so things
// like prototype overrides on Array work as expected.
data = this._JSONParse(data);
if (!this._handler) {
this._pendingMessagesIncoming.push(data);
}
else {
try {
this._handler({data: data,
__exposedProps__: {data: 'r'}
});
}
catch (ex) {
this._onerror(ex);
}
}
},
set onmessage(handler) { // property setter for onmessage
this._handler = handler;
while (this._pendingMessagesIncoming.length) {
this._onmessage(this._pendingMessagesIncoming.shift());
}
},
/**
* postMessage
*
* Send data to the onmessage handler on the other end of the port. The
* data object should have a topic property.
*
* @param {jsobj} data
*/
postMessage: function fw_AbstractPort_postMessage(data) {
if (this._portid === null) {
throw new Error("port is closed");
}
// There seems to be an issue with passing objects directly and letting
// the structured clone thing work - we sometimes get:
// [Exception... "The object could not be cloned." code: "25" nsresult: "0x80530019 (DataCloneError)"]
// The best guess is that this happens when funky things have been added to the prototypes.
// It doesn't happen for our "control" messages, only in messages from
// content - so we explicitly use JSON on these messages as that avoids
// the problem.
this._postControlMessage("port-message", JSON.stringify(data));
},
close: function fw_AbstractPort_close() {
if (!this._portid) {
return; // already closed.
}
this._postControlMessage("port-close");
// and clean myself up.
this._handler = null;
this._pendingMessagesIncoming = [];
this._portid = null;
}
}
// Note: this is never instantiated in chrome - the source is sent across
// to the worker and it is evaluated there and created in response to a
// port-create message we send.
function WorkerPort(portid) {
AbstractPort.call(this, portid);
}
WorkerPort.prototype = {
__proto__: AbstractPort.prototype,
_portType: "worker",
_dopost: function fw_WorkerPort_dopost(data) {
// postMessage is injected into the sandbox.
_postMessage(data, "*");
},
_onerror: function fw_WorkerPort_onerror(err) {
throw new Error("Port " + this + " handler failed: " + err);
}
}
/**
* ClientPort
*
* Client side of the entangled ports. The ClientPort is used by both XUL
* windows and Content windows to communicate with the worker
*
* constructor:
* @param {integer} portid
* @param {nsiDOMWindow} clientWindow, optional
*/
function ClientPort(portid, clientWindow) {
this._clientWindow = clientWindow
this._window = null;
// messages posted to the worker before the worker has loaded.
this._pendingMessagesOutgoing = [];
AbstractPort.call(this, portid);
}
ClientPort.prototype = {
__exposedProps__: {
'port': 'r',
'onmessage': 'rw',
'postMessage': 'r',
'close': 'r'
},
__proto__: AbstractPort.prototype,
_portType: "client",
_JSONParse: function fw_ClientPort_JSONParse(data) {
if (this._clientWindow) {
return this._clientWindow.JSON.parse(data);
}
return JSON.parse(data);
},
_createWorkerAndEntangle: function fw_ClientPort_createWorkerAndEntangle(worker) {
this._window = worker.frame.contentWindow;
worker.ports[this._portid] = this;
this._postControlMessage("port-create");
while (this._pendingMessagesOutgoing.length) {
this._dopost(this._pendingMessagesOutgoing.shift());
}
},
_dopost: function fw_ClientPort_dopost(data) {
if (!this._window) {
this._pendingMessagesOutgoing.push(data);
} else {
this._window.postMessage(data, "*");
}
},
_onerror: function fw_ClientPort_onerror(err) {
Cu.reportError("FrameWorker: Port " + this + " handler failed: " + err + "\n" + err.stack);
},
close: function fw_ClientPort_close() {
if (!this._portid) {
return; // already closed.
}
// a leaky abstraction due to the worker spec not specifying how the
// other end of a port knows it is closing.
this.postMessage({topic: "social.port-closing"});
AbstractPort.prototype.close.call(this);
this._window = null;
this._pendingMessagesOutgoing = null;
}
}
function importScripts() {
for (var i=0; i < arguments.length; i++) {
// load the url *synchronously*
var scriptURL = arguments[i];
var xhr = new XMLHttpRequest();
xhr.open('GET', scriptURL, false);
xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
eval(xhr.responseText);
}
else {
throw new Error("Unable to importScripts ["+scriptURL+"], status " + xhr.status)
}
}
};
xhr.send(null);
}
}

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

@ -10,6 +10,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \
FrameWorker.jsm \
SocialService.jsm \
$(NULL)

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

@ -10,6 +10,10 @@ relativesrcdir = toolkit/components/social/test
include $(DEPTH)/config/autoconf.mk
# mochitest tests
DIRS += browser \
$(NULL)
XPCSHELL_TESTS = \
xpcshell \
$(NULL)

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

@ -0,0 +1,28 @@
#
# 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/.
DEPTH = ../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = toolkit/components/social/test/browser
MODULE = test_socialapi
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
head.js \
data.json \
worker_xhr.js \
worker_localStorage.js \
browser_frameworker.js \
worker_relative.js \
relative_import.js \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

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

@ -0,0 +1,269 @@
let modules = {};
Cu.import("resource://gre/modules/FrameWorker.jsm", modules);
function makeWorkerUrl(runner) {
return "data:application/javascript," + encodeURI("let run=" + runner.toSource()) + ";run();"
}
function test() {
runTests(tests);
}
let tests = {
testSimple: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
port.postMessage({topic: "pong"});
}
}
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), undefined, "testSimple");
isnot(worker._worker.frame.contentWindow.toString(), "[object ChromeWindow]", "worker window isn't a chrome window");
worker.port.onmessage = function(e) {
if (e.data.topic == "pong") {
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "ping"})
},
// when the client closes early but the worker tries to send anyway...
testEarlyClose: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.postMessage({topic: "oh hai"});
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), undefined, "testEarlyClose");
worker.port.close();
worker.terminate();
cbnext();
},
// Check we do get a social.port-closing message as the port is closed.
testPortClosingMessage: function(cbnext) {
// We use 2 ports - we close the first and report success via the second.
let run = function() {
let firstPort, secondPort;
onconnect = function(e) {
let port = e.ports[0];
if (firstPort === undefined) {
firstPort = port;
port.onmessage = function(e) {
if (e.data.topic == "social.port-closing") {
secondPort.postMessage({topic: "got-closing"});
}
}
} else {
secondPort = port;
// now both ports are connected we can trigger the client side
// closing the first.
secondPort.postMessage({topic: "connected"});
}
}
}
let workerurl = makeWorkerUrl(run);
let worker1 = modules.FrameWorker(workerurl, undefined, "testPortClosingMessage worker1");
let worker2 = modules.FrameWorker(workerurl, undefined, "testPortClosingMessage worker2");
worker2.port.onmessage = function(e) {
if (e.data.topic == "connected") {
// both ports connected, so close the first.
worker1.port.close();
} else if (e.data.topic == "got-closing") {
worker2.terminate();
cbnext();
}
}
},
// Tests that prototypes added to core objects work with data sent over
// the message ports.
testPrototypes: function(cbnext) {
let run = function() {
// Modify the Array prototype...
Array.prototype.customfunction = function() {};
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
// Check the data we get via the port has the prototype modification
if (e.data.topic == "hello" && e.data.data.customfunction) {
port.postMessage({topic: "hello", data: [1,2,3]});
}
}
}
}
// hrmph - this kinda sucks as it is really just testing the actual
// implementation rather than the end result, but is OK for now.
// Really we are just testing that JSON.parse in the client window
// is called.
let fakeWindow = {
JSON: {
parse: function(s) {
let data = JSON.parse(s);
data.data.somextrafunction = function() {};
return data;
}
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), fakeWindow, "testPrototypes");
worker.port.onmessage = function(e) {
if (e.data.topic == "hello" && e.data.data.somextrafunction) {
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "hello", data: [1,2,3]});
},
testArrayUsingBuffer: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "go") {
let buffer = new ArrayBuffer(10);
// this one has always worked in the past, but worth checking anyway...
if (new Uint8Array(buffer).length != 10) {
port.postMessage({topic: "result", reason: "first length was not 10"});
return;
}
let reader = new FileReader();
reader.onload = function(event) {
if (new Uint8Array(buffer).length != 10) {
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
return;
}
// all seems good!
port.postMessage({topic: "result", reason: "ok"});
}
let blob = new Blob([buffer], {type: "binary"});
reader.readAsArrayBuffer(blob);
}
}
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), undefined, "testArray");
worker.port.onmessage = function(e) {
if (e.data.topic == "result") {
is(e.data.reason, "ok", "check the array worked");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "go"});
},
testArrayUsingReader: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "go") {
let buffer = new ArrayBuffer(10);
let reader = new FileReader();
reader.onload = function(event) {
try {
if (new Uint8Array(reader.result).length != 10) {
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
return;
}
// all seems good!
port.postMessage({topic: "result", reason: "ok"});
} catch (ex) {
port.postMessage({topic: "result", reason: ex.toString()});
}
}
let blob = new Blob([buffer], {type: "binary"});
reader.readAsArrayBuffer(blob);
}
}
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), undefined, "testArray");
worker.port.onmessage = function(e) {
if (e.data.topic == "result") {
is(e.data.reason, "ok", "check the array worked");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "go"});
},
testXHR: function(cbnext) {
// NOTE: this url MUST be in the same origin as worker_xhr.js fetches from!
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_xhr.js";
let worker = modules.FrameWorker(url, undefined, "testXHR");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check the xhr test worked");
worker.terminate();
cbnext();
}
}
},
testLocalStorage: function(cbnext) {
// NOTE: this url MUST be in the same origin as worker_xhr.js fetches from!
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_localStorage.js";
let worker = modules.FrameWorker(url, undefined, "testLocalStorage");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check the localStorage test worked");
worker.terminate();
cbnext();
}
}
},
testSameOriginImport: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
try {
importScripts("http://foo.bar/error");
} catch(ex) {
port.postMessage({topic: "pong", data: ex});
return;
}
port.postMessage({topic: "pong", data: null});
}
}
}
}
let worker = modules.FrameWorker(makeWorkerUrl(run), undefined, "testSameOriginImport");
worker.port.onmessage = function(e) {
if (e.data.topic == "pong") {
isnot(e.data.data, null, "check same-origin applied to importScripts");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "ping"})
},
testRelativeImport: function(cbnext) {
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_relative.js";
let worker = modules.FrameWorker(url, undefined, "testSameOriginImport");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check relative url in importScripts");
worker.terminate();
cbnext();
}
}
}
}

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

@ -0,0 +1,3 @@
{
"response": "ok"
}

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

@ -0,0 +1,51 @@
Cu.import("resource://gre/modules/Services.jsm");
// A helper to run a suite of tests.
// The "test object" should be an object with function names as keys and a
// function as the value. The functions will be called with a "cbnext" param
// which should be called when the test is complete.
// eg:
// test = {
// foo: function(cbnext) {... cbnext();}
// }
function runTests(tests, cbPreTest, cbPostTest) {
waitForExplicitFinish();
let testIter = Iterator(tests);
if (cbPreTest === undefined) {
cbPreTest = function(cb) {cb()};
}
if (cbPostTest === undefined) {
cbPostTest = function(cb) {cb()};
}
let runNextTest = function() {
let name, func;
try {
[name, func] = testIter.next();
} catch (err if err instanceof StopIteration) {
// out of items:
finish();
return;
}
// We run on a timeout as the frameworker also makes use of timeouts, so
// this helps keep the debug messages sane.
window.setTimeout(function() {
function cleanupAndRunNextTest() {
info("sub-test " + name + " complete");
cbPostTest(runNextTest);
}
cbPreTest(function() {
info("sub-test " + name + " starting");
try {
func.call(tests, cleanupAndRunNextTest);
} catch (ex) {
ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
cleanupAndRunNextTest();
}
})
}, 0)
}
runNextTest();
}

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

@ -0,0 +1 @@
dump("relative_import file\n");

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

@ -0,0 +1,17 @@
// Used to test XHR in the worker.
onconnect = function(e) {
let port = e.ports[0];
try {
localStorage.setItem("foo", "1");
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to set localStorage, " + e.toString() });
}
var ok;
try {
ok = localStorage["foo"] == 1;
} catch (e) {
ok = false;
}
port.postMessage({topic: "done", result: ok ? "ok" : "FAILED to read localStorage"});
}

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

@ -0,0 +1,13 @@
// Used to test XHR in the worker.
onconnect = function(e) {
let port = e.ports[0];
let req;
try {
importScripts("relative_import.js");
port.postMessage({topic: "done", result: "ok"});
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to importScripts, " + e.toString() });
return;
}
port.postMessage({topic: "done", result: "FAILED to importScripts, no exception" });
}

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

@ -0,0 +1,34 @@
// Used to test XHR in the worker.
onconnect = function(e) {
let port = e.ports[0];
let req;
try {
req = new XMLHttpRequest();
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to create XHR object, " + e.toString() });
}
if (req === undefined) { // until bug 756173 is fixed...
port.postMessage({topic: "done", result: "FAILED to create XHR object"});
return;
}
// The test that uses this worker MUST use the same origin to load the worker.
// We fetch the test app manifest so we can check the data is what we expect.
let url = "https://example.com/browser/toolkit/components/social/test/browser/data.json";
req.open("GET", url, true);
req.onreadystatechange = function() {
if (req.readyState === 4) {
let ok = req.status == 200 && req.responseText.length > 0;
if (ok) {
// check we actually got something sane...
try {
let data = JSON.parse(req.responseText);
ok = "response" in data;
} catch(e) {
ok = e.toString();
}
}
port.postMessage({topic: "done", result: ok ? "ok" : "bad response"});
}
}
req.send(null);
}

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

@ -62,6 +62,7 @@ EXTRA_JS_MODULES = \
PopupNotifications.jsm \
Dict.jsm \
PropertyListUtils.jsm \
PrivateBrowsingUtils.jsm \
$(NULL)
EXTRA_PP_JS_MODULES = \

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

@ -0,0 +1,16 @@
/* 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/. */
var EXPORTED_SYMBOLS = ["PrivateBrowsingUtils"];
const Ci = Components.interfaces;
var PrivateBrowsingUtils = {
isWindowPrivate: function pbu_isWindowPrivate(aWindow) {
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext)
.usePrivateBrowsing;
}
};

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

@ -55,16 +55,15 @@ function childFocused() {
function childResized() {
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
if (isOSXLion) {
todo_is(win.windowState, win.STATE_MAXIMIZED,
"A resize before being maximized breaks this test on 10.7");
finish();
return;
}
is(win.windowState, win.STATE_MAXIMIZED,
"window should be maximized");
if (isOSXLion) {
finish();
return;
}
isnot(win.innerWidth, 300,
"window inner width should have changed");

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

@ -37,6 +37,8 @@ namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const char *mach_port_name,
FilterCallback filter,
void *filter_context,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
@ -44,6 +46,8 @@ CrashGenerationServer::CrashGenerationServer(
bool generate_dumps,
const std::string &dump_path)
: dump_callback_(dump_callback),
filter_(filter),
filter_context_(filter_context),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
@ -110,7 +114,7 @@ bool CrashGenerationServer::WaitForOneMessage() {
bool result;
std::string dump_path;
if (generate_dumps_) {
if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
ScopedTaskSuspend suspend(remote_task);
MinidumpGenerator generator(remote_task, handler_thread);

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

@ -65,10 +65,14 @@ class CrashGenerationServer {
typedef void (*OnClientExitingCallback)(void *context,
const ClientInfo &client_info);
typedef bool (*FilterCallback)(void *context);
// Create an instance with the given parameters.
//
// mach_port_name: Named server port to listen on.
// filter: Callback for a client to know that we are about to write a dump
// and mabybe prevent it.
// filter_context: Context for the filte callback.
// dump_callback: Callback for a client crash dump request.
// dump_context: Context for client crash dump request callback.
// exit_callback: Callback for client process exit.
@ -80,6 +84,8 @@ class CrashGenerationServer {
// dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const char *mach_port_name,
FilterCallback filter,
void *filter_context,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
@ -109,6 +115,9 @@ class CrashGenerationServer {
// if a quit message was received or if an error occurred.
bool WaitForOneMessage();
FilterCallback filter_;
void *filter_context_;
OnClientDumpRequestCallback dump_callback_;
void *dump_context_;

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

@ -84,6 +84,7 @@ using mozilla::InjectCrashRunnable;
#include <vector>
#include "mozilla/mozalloc_oom.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(XP_MACOSX)
CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
@ -656,6 +657,14 @@ static bool ShouldReport()
return !(envvar && *envvar);
}
namespace {
bool Filter(void* context) {
mozilla::DisableWritePoisoning();
return true;
}
}
nsresult SetExceptionHandler(nsIFile* aXREDirectory,
bool force/*=false*/)
{
@ -814,7 +823,7 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
#ifdef XP_WIN
FPEFilter,
#else
nsnull,
Filter,
#endif
MinidumpCallback,
nsnull,
@ -1940,6 +1949,11 @@ OOPInitialized()
return pidToMinidump != NULL;
}
static bool ChildFilter(void *context) {
mozilla::DisableWritePoisoning();
return true;
}
static void
OOPInit()
{
@ -1990,6 +2004,8 @@ OOPInit()
crashServer = new CrashGenerationServer(
childCrashNotifyPipe,
ChildFilter,
NULL,
OnChildProcessDumpRequested, NULL,
NULL, NULL,
true, // automatically generate dumps
@ -2203,7 +2219,7 @@ SetRemoteExceptionHandler(const nsACString& crashPipe)
gExceptionHandler = new google_breakpad::
ExceptionHandler("",
NULL, // no filter callback
Filter,
NULL, // no minidump callback
NULL, // no callback context
true, // install signal handlers

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

@ -2,35 +2,67 @@
# 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/.
# all.js
# LOCALIZATION NOTE (general.useragent.locale):
# This is the valid BCP 47 language tag representing your locale.
#
# Localization Note: Cases of charset names must be preserved. If you're
# adding charsets to your localized version, please refer to
# intl/uconv/src/charsetalias.properties file for the list of canonical
# charset names and use canonical names exactly as listed there.
# Also note that "UTF-8" should always be included in intl.charsetmenu.browser.static
# In most cases, this will simply be your locale code. However, in rare cases
# (such as 'jp-JP-mac'), you may need to modify your locale code in order to
# make it a valid BCP 47 language tag. (If your locale code does not include a
# region subtag, do not include one in the language tag representing your
# locale.)
general.useragent.locale=en-US
# LOCALIZATION NOTE (intl.accept_languages):
# This is a comma-separated list of valid BCP 47 language tags.
#
# Begin with the value of 'general.useragent.locale'. Next, include language
# tags for other languages that you expect most users of your locale to be
# able to speak, so that their browsing experience degrades gracefully if
# content is not available in their primary language.
#
# It is recommended that you include "en-US, en" at the end of the list as a
# last resort. However, if you know that users of your locale would prefer a
# different variety of English, or if they are not likely to understand
# English at all, you may opt to include a different English language tag, or
# to exclude English altogether.
#
# For example, the Breton [br] locale might consider including French and
# British English in their list, since those languages are commonly spoken in
# the same area as Breton:
# intl.accept_languages=br, fr-FR, fr, en-GB, en
intl.accept_languages=en-US, en
# LOCALIZATION NOTE (font.language.group):
# This preference controls the initial setting of the language drop-down menu
# in the Content > Fonts & Colors > Advanced preference panel.
#
# Set it to the value of one of the menuitems in the "selectLangs" menulist in
# http://mxr.mozilla.org/mozilla/source/browser/components/preferences/fonts.xul
font.language.group=x-western
# LOCALIZATION NOTE (intl.charset.detector, intl.charset.default, intl.charsetmenu.browser.static, intl.charsetmenu.mailedit):
# For the list of canonical charset values, refer to:
# http://mxr.mozilla.org/mozilla-central/source/intl/locale/src/charsetalias.properties
#
# If you are modifying the charset preferences for your locale, be sure to use
# the values from that list, not the keys. Note that these values are
# case-sensitive and must be reproduced exactly as listed.
#
# Note also that the list of charsets in 'intl.charsetmenu.browser.static'
# must always include "UTF-8".
intl.charset.detector=
intl.charset.default=ISO-8859-1
intl.charsetmenu.browser.static=ISO-8859-1, UTF-8
intl.charsetmenu.mailedit=ISO-8859-1, ISO-8859-15, ISO-8859-6, armscii-8, ISO-8859-13, ISO-8859-14, ISO-8859-2, GB2312, GB18030, Big5, KOI8-R, windows-1251, KOI8-U, ISO-8859-7, ISO-8859-8-I, windows-1255, ISO-2022-JP, EUC-KR, ISO-8859-10, ISO-8859-3, TIS-620, ISO-8859-9, UTF-8, VISCII
# LOCALIZATION NOTE (pluralRule): Pick the appropriate plural rule for your
# language. This will determine how many plural forms of a word you will need
# to provide and in what order.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
pluralRule=1
# Localization Note: font.language.group controls the initial setting of the
# language drop-down in the fonts pref panel. Set it to the value of one of the
# menuitems in the "selectLangs" menulist in
# browser/components/preferences/fonts.xul
font.language.group=x-western
# Localization Note: Add the code for your language at the front of this entry,
# leaving "en-us, en" for fallback. It's recommended to use the same form, e.g.
# "ja-jp, ja, en-us, en"
intl.accept_languages=en-us, en
intl.charsetmenu.browser.static=ISO-8859-1, UTF-8
intl.charset.default=ISO-8859-1
intl.charset.detector=
intl.charsetmenu.mailedit=ISO-8859-1, ISO-8859-15, ISO-8859-6, armscii-8, ISO-8859-13, ISO-8859-14, ISO-8859-2, GB2312, GB18030, Big5, KOI8-R, windows-1251, KOI8-U, ISO-8859-7, ISO-8859-8-I, windows-1255, ISO-2022-JP, EUC-KR, ISO-8859-10, ISO-8859-3, TIS-620, ISO-8859-9, UTF-8, VISCII
# valid intl.menuitems.appendedacceskeys are: true or false, <empty string> (missing or empty preference equals false)
# LOCALIZATION NOTE (intl.menuitems.alwaysappendaccesskeys, intl.menuitems.insertseparatorbeforeaccesskeys):
# Valid values are: true, false, <empty string>
# Missing preference or empty value equals false.
intl.menuitems.alwaysappendaccesskeys=
# valid intl.menuitems.insertseparatorbeforeaccesskeys are: true or false, <empty string> (missing or empty preference equals false)
intl.menuitems.insertseparatorbeforeaccesskeys=true

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

@ -35,6 +35,7 @@
#include "nsStackWalk.h"
#include "nsTraceMallocCallbacks.h"
#include "nsTypeInfo.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(XP_MACOSX)
@ -1329,6 +1330,9 @@ NS_TraceMallocStartup(int logfd)
PR_ASSERT(logfp == &default_logfile);
tracing_enabled = (logfd >= 0);
if (logfd >= 3)
MozillaRegisterDebugFD(logfd);
/* stacks are disabled if this env var is set to a non-empty value */
stack_disable_env = PR_GetEnv("NS_TRACE_MALLOC_DISABLE_STACKS");
stacks_enabled = !stack_disable_env || !*stack_disable_env;
@ -1527,6 +1531,7 @@ NS_TraceMallocShutdown(void)
log_tmstats(fp);
flush_logfile(fp);
if (fp->fd >= 0) {
MozillaUnRegisterDebugFD(fp->fd);
close(fp->fd);
fp->fd = -1;
}
@ -1663,6 +1668,7 @@ NS_TraceMallocCloseLogFD(int fd)
}
TM_EXIT_LOCK_AND_UNSUPPRESS_TRACING(t);
MozillaUnRegisterDebugFD(fd);
close(fd);
}
@ -1757,6 +1763,7 @@ NS_TraceMallocDumpAllocations(const char *pathname)
{
FILE *ofp;
int rv;
int fd;
tm_thread *t = tm_get_thread();
@ -1764,12 +1771,15 @@ NS_TraceMallocDumpAllocations(const char *pathname)
ofp = fopen(pathname, WRITE_FLAGS);
if (ofp) {
MozillaRegisterDebugFD(fileno(ofp));
if (allocations) {
PL_HashTableEnumerateEntries(allocations, allocation_enumerator,
ofp);
}
rv = ferror(ofp) ? -1 : 0;
fclose(ofp);
fd = fileno(ofp);
fclose(ofp); /* May call write. */
MozillaUnRegisterDebugFD(fd);
} else {
rv = -1;
}

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

@ -341,7 +341,7 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion,
printf("--COMPOSITE-- %p\n", mPresShell);
#endif
mPresShell->Paint(aView, damageRegion, nsIPresShell::PaintType_Composite,
aWillSendDidPaint);
false);
#ifdef DEBUG_INVALIDATIONS
printf("--ENDCOMPOSITE--\n");
#endif
@ -387,6 +387,27 @@ bool nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
aView->SkippedPaint();
skipped = true;
} else {
// If an ancestor widget was hidden and then shown, we could
// have a delayed resize to handle.
for (nsViewManager *vm = this; vm;
vm = vm->mRootView->GetParent()
? vm->mRootView->GetParent()->GetViewManager()
: nsnull) {
if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
vm->mRootView->IsEffectivelyVisible() &&
mPresShell && mPresShell->IsVisible()) {
vm->FlushDelayedResize(true);
vm->InvalidateView(vm->mRootView);
}
}
// Flush things like reflows and plugin widget geometry updates by
// calling WillPaint on observer presShells.
nsRefPtr<nsViewManager> rootVM = RootViewManager();
if (mPresShell) {
rootVM->CallWillPaintOnObservers(true);
}
aView->ClearSkippedPaints();
aView->SetPendingRefresh(false);
widget->SetNeedsPaint(false);
@ -395,11 +416,13 @@ bool nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
#endif
nsAutoScriptBlocker scriptBlocker;
mPresShell->Paint(aView, nsRegion(), nsIPresShell::PaintType_NoComposite, false);
mPresShell->Paint(aView, nsRegion(), nsIPresShell::PaintType_NoComposite, true);
#ifdef DEBUG_INVALIDATIONS
printf("---- PAINT END ----\n");
#endif
SetPainting(false);
rootVM->CallDidPaintOnObserver();
}
}
FlushDirtyRegionToWidget(aView);
@ -732,35 +755,6 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
break;
*aStatus = nsEventStatus_eConsumeNoDefault;
nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
NS_ASSERTION(static_cast<nsView*>(aView) ==
nsView::GetViewFor(event->widget),
"view/widget mismatch");
// If an ancestor widget was hidden and then shown, we could
// have a delayed resize to handle.
for (nsViewManager *vm = this; vm;
vm = vm->mRootView->GetParent()
? vm->mRootView->GetParent()->GetViewManager()
: nsnull) {
if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
vm->mRootView->IsEffectivelyVisible() &&
mPresShell && mPresShell->IsVisible()) {
vm->FlushDelayedResize(true);
vm->InvalidateView(vm->mRootView);
}
}
// Flush things like reflows and plugin widget geometry updates by
// calling WillPaint on observer presShells.
nsRefPtr<nsViewManager> rootVM = RootViewManager();
if (mPresShell) {
rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
}
// Flush view widget geometry updates and invalidations.
rootVM->ProcessPendingUpdates();
}
break;
@ -778,17 +772,6 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
"shouldn't be receiving paint events while painting is "
"disallowed!");
if (!event->didSendWillPaint) {
// Send NS_WILL_PAINT event ourselves.
nsPaintEvent willPaintEvent(true, NS_WILL_PAINT, event->widget);
willPaintEvent.willSendDidPaint = event->willSendDidPaint;
DispatchEvent(&willPaintEvent, view, aStatus);
// Get the view pointer again since NS_WILL_PAINT might have
// destroyed it during CallWillPaintOnObservers (bug 378273).
view = nsView::GetViewFor(event->widget);
}
if (!view || event->region.IsEmpty())
break;
@ -799,8 +782,6 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
}
case NS_DID_PAINT: {
nsRefPtr<nsViewManager> rootVM = RootViewManager();
rootVM->CallDidPaintOnObserver();
break;
}

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

@ -57,7 +57,7 @@ AndroidBridge::ConstructBridge(JNIEnv *jEnv,
* to call dlclose() while we're already inside dlclose().
* Conveniently, NSS has an env var that can prevent it from unloading.
*/
putenv("NSS_DISABLE_UNLOAD=1");
putenv("NSS_DISABLE_UNLOAD=1");
sBridge = new AndroidBridge();
if (!sBridge->Init(jEnv, jGeckoAppShellClass)) {
@ -128,7 +128,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jScanMedia = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V");
jGetSystemColors = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getSystemColors", "()[I");
jGetIconForExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getIconForExtension", "(Ljava/lang/String;I)[B");
jFireAndWaitForTracerEvent = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "fireAndWaitForTracerEvent", "()V");
jFireAndWaitForTracerEvent = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "fireAndWaitForTracerEvent", "()V");
jCreateShortcut = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jGetShowPasswordSetting = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getShowPasswordSetting", "()Z");
jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V");
@ -239,10 +239,17 @@ AndroidBridge::NotifyIME(int aType, int aState)
return;
AutoLocalJNIFrame jniFrame(env, 0);
env->CallStaticVoidMethod(sBridge->mGeckoAppShellClass,
env->CallStaticVoidMethod(sBridge->mGeckoAppShellClass,
sBridge->jNotifyIME, aType, aState);
}
jstring NewJavaString(AutoLocalJNIFrame* frame, const PRUnichar* string, PRUint32 len) {
jstring ret = frame->GetEnv()->NewString( string, len);
if (frame->CheckForException())
return NULL;
return ret;
}
void
AndroidBridge::NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
const nsAString& aActionHint)
@ -261,8 +268,8 @@ AndroidBridge::NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
jvalue args[4];
args[0].i = aState;
args[1].l = env->NewString(typeHint.get(), typeHint.Length());
args[2].l = env->NewString(actionHint.get(), actionHint.Length());
args[1].l = NewJavaString(&jniFrame, typeHint.get(), typeHint.Length());
args[2].l = NewJavaString(&jniFrame, actionHint.get(), actionHint.Length());
args[3].z = false;
PRInt32 landscapeFS;
@ -271,11 +278,11 @@ AndroidBridge::NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
args[3].z = true;
} else if (landscapeFS == -1){
if (NS_SUCCEEDED(
Preferences::GetInt(IME_FULLSCREEN_THRESHOLD_PREF,
&landscapeFS))) {
// the threshold is hundreths of inches, so convert the
Preferences::GetInt(IME_FULLSCREEN_THRESHOLD_PREF,
&landscapeFS))) {
// the threshold is hundreths of inches, so convert the
// threshold to pixels and multiply the height by 100
if (nsWindow::GetAndroidScreenBounds().height * 100 <
if (nsWindow::GetAndroidScreenBounds().height * 100 <
landscapeFS * Bridge()->GetDPI()) {
args[3].z = true;
}
@ -285,7 +292,7 @@ AndroidBridge::NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
}
env->CallStaticVoidMethodA(sBridge->mGeckoAppShellClass,
sBridge->jNotifyIMEEnabled, args);
sBridge->jNotifyIMEEnabled, args);
}
void
@ -302,12 +309,12 @@ AndroidBridge::NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen,
AutoLocalJNIFrame jniFrame(env);
jvalue args[4];
args[0].l = env->NewString(aText, aTextLen);
args[0].l = NewJavaString(&jniFrame, aText, aTextLen);
args[1].i = aStart;
args[2].i = aEnd;
args[3].i = aNewEnd;
env->CallStaticVoidMethodA(sBridge->mGeckoAppShellClass,
sBridge->jNotifyIMEChange, args);
sBridge->jNotifyIMEChange, args);
}
void
@ -383,7 +390,7 @@ AndroidBridge::ReturnIMEQueryResult(const PRUnichar *aResult, PRUint32 aLen,
AutoLocalJNIFrame jniFrame(env);
jvalue args[3];
args[0].l = env->NewString(aResult, aLen);
args[0].l = NewJavaString(&jniFrame, aResult, aLen);
args[1].i = aSelStart;
args[2].i = aSelLen;
env->CallStaticVoidMethodA(mGeckoAppShellClass,
@ -416,7 +423,7 @@ AndroidBridge::NotifyXreExit()
env->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyXreExit);
}
static void
static void
getHandlersFromStringArray(JNIEnv *aJNIEnv, jobjectArray jArr, jsize aLen,
nsIMutableArray *aHandlersArray,
nsIHandlerApp **aDefaultApp,
@ -425,18 +432,18 @@ getHandlersFromStringArray(JNIEnv *aJNIEnv, jobjectArray jArr, jsize aLen,
{
nsString empty = EmptyString();
for (jsize i = 0; i < aLen; i+=4) {
nsJNIString name(
nsJNIString name(
static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i)), aJNIEnv);
nsJNIString isDefault(
static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 1)), aJNIEnv);
nsJNIString packageName(
nsJNIString packageName(
static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 2)), aJNIEnv);
nsJNIString className(
nsJNIString className(
static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 3)), aJNIEnv);
nsIHandlerApp* app = nsOSHelperAppService::
CreateAndroidHandlerApp(name, className, packageName,
className, aMimeType, aAction);
aHandlersArray->AppendElement(app, false);
if (aDefaultApp && isDefault.Length() > 0)
*aDefaultApp = app;
@ -455,17 +462,17 @@ AndroidBridge::GetHandlersForMimeType(const char *aMimeType,
if (!env)
return false;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
jstring jstrMimeType =
env->NewString(wMimeType.get(), wMimeType.Length());
NewJavaString(&jniFrame, wMimeType.get(), wMimeType.Length());
jstring jstrAction = env->NewString(nsPromiseFlatString(aAction).get(),
aAction.Length());
jstring jstrAction = NewJavaString(&jniFrame, nsPromiseFlatString(aAction).get(),
aAction.Length());
jobject obj = env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetHandlersForMimeType,
jstrMimeType, jstrAction);
jGetHandlersForMimeType,
jstrMimeType, jstrAction);
if (jniFrame.CheckForException())
return false;
@ -478,7 +485,7 @@ AndroidBridge::GetHandlersForMimeType(const char *aMimeType,
if (!aHandlersArray)
return len > 0;
getHandlersFromStringArray(env, arr, len, aHandlersArray,
getHandlersFromStringArray(env, arr, len, aHandlersArray,
aDefaultApp, aAction,
nsDependentCString(aMimeType));
return true;
@ -496,15 +503,15 @@ AndroidBridge::GetHandlersForURL(const char *aURL,
if (!env)
return false;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertUTF8toUTF16 wScheme(aURL);
jstring jstrScheme = env->NewString(wScheme.get(), wScheme.Length());
jstring jstrAction = env->NewString(nsPromiseFlatString(aAction).get(),
aAction.Length());
jstring jstrScheme = NewJavaString(&jniFrame, wScheme.get(), wScheme.Length());
jstring jstrAction = NewJavaString(&jniFrame, nsPromiseFlatString(aAction).get(),
aAction.Length());
jobject obj = env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetHandlersForURL,
jstrScheme, jstrAction);
jGetHandlersForURL,
jstrScheme, jstrAction);
if (jniFrame.CheckForException())
return false;
@ -517,7 +524,7 @@ AndroidBridge::GetHandlersForURL(const char *aURL,
if (!aHandlersArray)
return len > 0;
getHandlersFromStringArray(env, arr, len, aHandlersArray,
getHandlersFromStringArray(env, arr, len, aHandlersArray,
aDefaultApp, aAction);
return true;
}
@ -533,25 +540,25 @@ AndroidBridge::OpenUriExternal(const nsACString& aUriSpec, const nsACString& aMi
if (!env)
return false;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertUTF8toUTF16 wUriSpec(aUriSpec);
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
jstring jstrUri = env->NewString(wUriSpec.get(), wUriSpec.Length());
jstring jstrType = env->NewString(wMimeType.get(), wMimeType.Length());
jstring jstrUri = NewJavaString(&jniFrame, wUriSpec.get(), wUriSpec.Length());
jstring jstrType = NewJavaString(&jniFrame, wMimeType.get(), wMimeType.Length());
jstring jstrPackage = env->NewString(nsPromiseFlatString(aPackageName).get(),
aPackageName.Length());
jstring jstrClass = env->NewString(nsPromiseFlatString(aClassName).get(),
aClassName.Length());
jstring jstrAction = env->NewString(nsPromiseFlatString(aAction).get(),
aAction.Length());
jstring jstrTitle = env->NewString(nsPromiseFlatString(aTitle).get(),
aTitle.Length());
jstring jstrPackage = NewJavaString(&jniFrame, nsPromiseFlatString(aPackageName).get(),
aPackageName.Length());
jstring jstrClass = NewJavaString(&jniFrame, nsPromiseFlatString(aClassName).get(),
aClassName.Length());
jstring jstrAction = NewJavaString(&jniFrame, nsPromiseFlatString(aAction).get(),
aAction.Length());
jstring jstrTitle = NewJavaString(&jniFrame, nsPromiseFlatString(aTitle).get(),
aTitle.Length());
bool ret = env->CallStaticBooleanMethod(mGeckoAppShellClass,
jOpenUriExternal,
jstrUri, jstrType, jstrPackage,
jstrUri, jstrType, jstrPackage,
jstrClass, jstrAction, jstrTitle);
if (jniFrame.CheckForException())
return false;
@ -568,13 +575,13 @@ AndroidBridge::GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString&
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertUTF8toUTF16 wFileExt(aFileExt);
jstring jstrExt = env->NewString(wFileExt.get(), wFileExt.Length());
jstring jstrExt = NewJavaString(&jniFrame, wFileExt.get(), wFileExt.Length());
jstring jstrType = static_cast<jstring>(
env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetMimeTypeFromExtensions,
jstrExt));
jGetMimeTypeFromExtensions,
jstrExt));
if (jniFrame.CheckForException())
return;
@ -591,13 +598,13 @@ AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType, nsACString&
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
jstring jstrType = env->NewString(wMimeType.get(), wMimeType.Length());
jstring jstrType = NewJavaString(&jniFrame, wMimeType.get(), wMimeType.Length());
jstring jstrExt = static_cast<jstring>(
env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetExtensionFromMimeType,
jstrType));
jGetExtensionFromMimeType,
jstrType));
if (jniFrame.CheckForException())
return;
@ -648,9 +655,9 @@ AndroidBridge::SetClipboardText(const nsAString& aText)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstr = env->NewString(nsPromiseFlatString(aText).get(),
aText.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jstr = NewJavaString(&jniFrame, nsPromiseFlatString(aText).get(),
aText.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jSetClipboardText, jstr);
}
@ -700,17 +707,17 @@ AndroidBridge::ShowAlertNotification(const nsAString& aImageUrl,
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
if (nsAppShell::gAppShell && aAlertListener)
nsAppShell::gAppShell->AddObserver(aAlertName, aAlertListener);
jvalue args[5];
args[0].l = env->NewString(nsPromiseFlatString(aImageUrl).get(), aImageUrl.Length());
args[1].l = env->NewString(nsPromiseFlatString(aAlertTitle).get(), aAlertTitle.Length());
args[2].l = env->NewString(nsPromiseFlatString(aAlertText).get(), aAlertText.Length());
args[3].l = env->NewString(nsPromiseFlatString(aAlertCookie).get(), aAlertCookie.Length());
args[4].l = env->NewString(nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
args[0].l = NewJavaString(&jniFrame, nsPromiseFlatString(aImageUrl).get(), aImageUrl.Length());
args[1].l = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertTitle).get(), aAlertTitle.Length());
args[2].l = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertText).get(), aAlertText.Length());
args[3].l = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertCookie).get(), aAlertCookie.Length());
args[4].l = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
env->CallStaticVoidMethodA(mGeckoAppShellClass, jShowAlertNotification, args);
}
@ -725,12 +732,12 @@ AndroidBridge::AlertsProgressListener_OnProgress(const nsAString& aAlertName,
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
jstring jstrName = env->NewString(nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
jstring jstrText = env->NewString(nsPromiseFlatString(aAlertText).get(), aAlertText.Length());
jstring jstrName = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
jstring jstrText = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertText).get(), aAlertText.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jAlertsProgressListener_OnProgress,
jstrName, aProgress, aProgressMax, jstrText);
jstrName, aProgress, aProgressMax, jstrText);
}
void
@ -742,9 +749,9 @@ AndroidBridge::AlertsProgressListener_OnCancel(const nsAString& aAlertName)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
jstring jstrName = env->NewString(nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
jstring jstrName = NewJavaString(&jniFrame, nsPromiseFlatString(aAlertName).get(), aAlertName.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jAlertsProgressListener_OnCancel, jstrName);
}
@ -775,12 +782,12 @@ AndroidBridge::ShowFilePickerForExtensions(nsAString& aFilePath, const nsAString
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrFilers = env->NewString(nsPromiseFlatString(aExtensions).get(),
aExtensions.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jstrFilers = NewJavaString(&jniFrame, nsPromiseFlatString(aExtensions).get(),
aExtensions.Length());
jstring jstr = static_cast<jstring>(env->CallStaticObjectMethod(
mGeckoAppShellClass,
jShowFilePickerForExtensions, jstrFilers));
mGeckoAppShellClass,
jShowFilePickerForExtensions, jstrFilers));
if (jniFrame.CheckForException())
return;
@ -796,9 +803,9 @@ AndroidBridge::ShowFilePickerForMimeType(nsAString& aFilePath, const nsAString&
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrFilers = env->NewString(nsPromiseFlatString(aMimeType).get(),
aMimeType.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jstrFilers = NewJavaString(&jniFrame, nsPromiseFlatString(aMimeType).get(),
aMimeType.Length());
jstring jstr = static_cast<jstring>(env->CallStaticObjectMethod(
mGeckoAppShellClass,
jShowFilePickerForMimeType, jstrFilers));
@ -815,9 +822,9 @@ AndroidBridge::ShowFilePickerAsync(const nsAString& aMimeType, nsFilePickerCallb
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jMimeType = env->NewString(nsPromiseFlatString(aMimeType).get(),
aMimeType.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jMimeType = NewJavaString(&jniFrame, nsPromiseFlatString(aMimeType).get(),
aMimeType.Length());
callback->AddRef();
env->CallStaticVoidMethod(mGeckoAppShellClass, jShowFilePickerAsync, jMimeType, (jlong) callback);
}
@ -865,7 +872,7 @@ AndroidBridge::PerformHapticFeedback(bool aIsLongPress)
AutoLocalJNIFrame jniFrame(env, 0);
env->CallStaticVoidMethod(mGeckoAppShellClass,
jPerformHapticFeedback, aIsLongPress);
jPerformHapticFeedback, aIsLongPress);
}
void
@ -918,7 +925,7 @@ AndroidBridge::Vibrate(const nsTArray<PRUint32>& aPattern)
env->ReleaseLongArrayElements(array, elts, 0);
env->CallStaticVoidMethod(mGeckoAppShellClass, jVibrateA,
array, -1/*don't repeat*/);
array, -1/*don't repeat*/);
// GC owns |array| now?
}
@ -976,8 +983,8 @@ AndroidBridge::SetSelectedLocale(const nsAString& aLocale)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jLocale = env->NewString(PromiseFlatString(aLocale).get(), aLocale.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jLocale = NewJavaString(&jniFrame, PromiseFlatString(aLocale).get(), aLocale.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jSetSelectedLocale, jLocale);
}
@ -994,7 +1001,7 @@ AndroidBridge::GetSystemColors(AndroidSystemColors *aColors)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
jobject obj = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetSystemColors);
if (jniFrame.CheckForException())
@ -1036,12 +1043,12 @@ AndroidBridge::GetIconForExtension(const nsACString& aFileExt, PRUint32 aIconSiz
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
nsString fileExt;
CopyUTF8toUTF16(aFileExt, fileExt);
jstring jstrFileExt = env->NewString(nsPromiseFlatString(fileExt).get(), fileExt.Length());
jstring jstrFileExt = NewJavaString(&jniFrame, nsPromiseFlatString(fileExt).get(), fileExt.Length());
jobject obj = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetIconForExtension, jstrFileExt, aIconSize);
if (jniFrame.CheckForException())
return;
@ -1273,12 +1280,12 @@ AndroidBridge::ScanMedia(const nsAString& aFile, const nsACString& aMimeType)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrFile = env->NewString(nsPromiseFlatString(aFile).get(), aFile.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jstrFile = NewJavaString(&jniFrame, nsPromiseFlatString(aFile).get(), aFile.Length());
nsString mimeType2;
CopyUTF8toUTF16(aMimeType, mimeType2);
jstring jstrMimeTypes = env->NewString(nsPromiseFlatString(mimeType2).get(), mimeType2.Length());
jstring jstrMimeTypes = NewJavaString(&jniFrame, nsPromiseFlatString(mimeType2).get(), mimeType2.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jScanMedia, jstrFile, jstrMimeTypes);
}
@ -1290,15 +1297,15 @@ AndroidBridge::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, co
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrTitle = env->NewString(nsPromiseFlatString(aTitle).get(), aTitle.Length());
jstring jstrURI = env->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
jstring jstrIconData = env->NewString(nsPromiseFlatString(aIconData).get(), aIconData.Length());
jstring jstrIntent = env->NewString(nsPromiseFlatString(aIntent).get(), aIntent.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jstrTitle = NewJavaString(&jniFrame, nsPromiseFlatString(aTitle).get(), aTitle.Length());
jstring jstrURI = NewJavaString(&jniFrame, nsPromiseFlatString(aURI).get(), aURI.Length());
jstring jstrIconData = NewJavaString(&jniFrame, nsPromiseFlatString(aIconData).get(), aIconData.Length());
jstring jstrIntent = NewJavaString(&jniFrame, nsPromiseFlatString(aIntent).get(), aIntent.Length());
if (!jstrURI || !jstrTitle || !jstrIconData)
return;
env->CallStaticVoidMethod(mGeckoAppShellClass, jCreateShortcut, jstrTitle, jstrURI, jstrIconData, jstrIntent);
}
@ -1393,7 +1400,7 @@ AndroidBridge::FireAndWaitForTracerEvent() {
return;
AutoLocalJNIFrame jniFrame(env, 0);
env->CallStaticVoidMethod(mGeckoAppShellClass,
env->CallStaticVoidMethod(mGeckoAppShellClass,
jFireAndWaitForTracerEvent);
}
@ -1405,7 +1412,7 @@ namespace mozilla {
mTracerLock = new Mutex("TracerRunnable");
mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable");
mMainThread = do_GetMainThread();
}
~TracerRunnable() {
delete mTracerCondVar;
@ -1418,13 +1425,13 @@ namespace mozilla {
MutexAutoLock lock(*mTracerLock);
if (!AndroidBridge::Bridge())
return NS_OK;
AndroidBridge::Bridge()->FireAndWaitForTracerEvent();
mHasRun = true;
mTracerCondVar->Notify();
return NS_OK;
}
bool Fire() {
if (!mTracerLock || !mTracerCondVar)
return false;
@ -1468,11 +1475,11 @@ namespace mozilla {
return false;
}
void SignalTracerThread()
{
if (sTracerRunnable)
return sTracerRunnable->Signal();
}
void SignalTracerThread()
{
if (sTracerRunnable)
return sTracerRunnable->Signal();
}
}
bool
@ -1522,10 +1529,10 @@ AndroidBridge::InitCamera(const nsCString& contentType, PRUint32 camera, PRUint3
if (!env)
return false;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
NS_ConvertASCIItoUTF16 s(contentType);
jstring jstrContentType = env->NewString(s.get(), NS_strlen(s.get()));
jstring jstrContentType = NewJavaString(&jniFrame, s.get(), NS_strlen(s.get()));
jobject obj = env->CallStaticObjectMethod(mGeckoAppShellClass, jInitCamera, jstrContentType, camera, *width, *height);
if (jniFrame.CheckForException())
@ -1595,7 +1602,7 @@ AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInf
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
// To prevent calling too many methods through JNI, the Java method returns
// an array of double even if we actually want a double and a boolean.
@ -1627,7 +1634,7 @@ AndroidBridge::HandleGeckoMessage(const nsAString &aMessage, nsAString &aRet)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jMessage = env->NewString(nsPromiseFlatString(aMessage).get(), aMessage.Length());
jstring jMessage = NewJavaString(&jniFrame, nsPromiseFlatString(aMessage).get(), aMessage.Length());
jstring returnMessage = static_cast<jstring>(env->CallStaticObjectMethod(mGeckoAppShellClass, jHandleGeckoMessage, jMessage));
if (jniFrame.CheckForException())
return;
@ -1644,9 +1651,11 @@ AndroidBridge::CheckURIVisited(const nsAString& aURI)
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrURI = env->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jCheckUriVisited, jstrURI);
AutoLocalJNIFrame jniFrame(env, 1);
jstring jstrURI = NewJavaString(&jniFrame, nsPromiseFlatString(aURI).get(), aURI.Length());
// If creating the string fails, just bail
if (jstrURI)
env->CallStaticVoidMethod(mGeckoAppShellClass, jCheckUriVisited, jstrURI);
}
void
@ -1657,7 +1666,7 @@ AndroidBridge::MarkURIVisited(const nsAString& aURI)
return;
AutoLocalJNIFrame jniFrame(env);
jstring jstrURI = env->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
jstring jstrURI = NewJavaString(&jniFrame, nsPromiseFlatString(aURI).get(), aURI.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jMarkUriVisited, jstrURI);
}
@ -1670,8 +1679,8 @@ AndroidBridge::GetNumberOfMessagesForText(const nsAString& aText)
if (!env)
return 0;
AutoLocalJNIFrame jniFrame(env);
jstring jText = env->NewString(PromiseFlatString(aText).get(), aText.Length());
AutoLocalJNIFrame jniFrame(env);
jstring jText = NewJavaString(&jniFrame, PromiseFlatString(aText).get(), aText.Length());
PRUint16 ret = env->CallStaticIntMethod(mGeckoAppShellClass, jNumberOfMessages, jText);
if (jniFrame.CheckForException())
return 0;
@ -1689,15 +1698,15 @@ AndroidBridge::SendMessage(const nsAString& aNumber, const nsAString& aMessage,
return;
AutoLocalJNIFrame jniFrame(env);
jstring jNumber = env->NewString(PromiseFlatString(aNumber).get(), aNumber.Length());
jstring jMessage = env->NewString(PromiseFlatString(aMessage).get(), aMessage.Length());
jstring jNumber = NewJavaString(&jniFrame, PromiseFlatString(aNumber).get(), aNumber.Length());
jstring jMessage = NewJavaString(&jniFrame, PromiseFlatString(aMessage).get(), aMessage.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jSendMessage, jNumber, jMessage, aRequestId, aProcessId);
}
PRInt32
AndroidBridge::SaveSentMessage(const nsAString& aRecipient,
const nsAString& aBody, PRUint64 aDate)
const nsAString& aBody, PRUint64 aDate)
{
ALOG_BRIDGE("AndroidBridge::SaveSentMessage");
@ -1706,8 +1715,8 @@ AndroidBridge::SaveSentMessage(const nsAString& aRecipient,
return 0;
AutoLocalJNIFrame jniFrame(env);
jstring jRecipient = env->NewString(PromiseFlatString(aRecipient).get(), aRecipient.Length());
jstring jBody = env->NewString(PromiseFlatString(aBody).get(), aBody.Length());
jstring jRecipient = NewJavaString(&jniFrame, PromiseFlatString(aRecipient).get(), aRecipient.Length());
jstring jBody = NewJavaString(&jniFrame, PromiseFlatString(aBody).get(), aBody.Length());
PRInt32 ret = env->CallStaticIntMethod(mGeckoAppShellClass, jSaveSentMessage, jRecipient, jBody, aDate);
if (jniFrame.CheckForException())
return 0;
@ -1751,12 +1760,12 @@ AndroidBridge::CreateMessageList(const dom::sms::SmsFilterData& aFilter, bool aR
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
jobjectArray numbers =
(jobjectArray)env->NewObjectArray(aFilter.numbers().Length(),
jStringClass,
env->NewStringUTF(""));
jStringClass,
env->NewStringUTF(""));
for (PRUint32 i = 0; i < aFilter.numbers().Length(); ++i) {
env->SetObjectArrayElement(numbers, i,
@ -1764,10 +1773,10 @@ AndroidBridge::CreateMessageList(const dom::sms::SmsFilterData& aFilter, bool aR
}
env->CallStaticVoidMethod(mGeckoAppShellClass, jCreateMessageList,
aFilter.startDate(), aFilter.endDate(),
numbers, aFilter.numbers().Length(),
aFilter.delivery(), aReverse, aRequestId,
aProcessId);
aFilter.startDate(), aFilter.endDate(),
numbers, aFilter.numbers().Length(),
aFilter.delivery(), aReverse, aRequestId,
aProcessId);
}
void
@ -1805,7 +1814,7 @@ AndroidBridge::GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInf
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
AutoLocalJNIFrame jniFrame(env);
// To prevent calling too many methods through JNI, the Java method returns
// an array of double even if we actually want a double and a boolean.
@ -1970,7 +1979,7 @@ AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *h
int err;
*bits = NULL;
*width = *height = *format = 0;
if (mHasNativeWindowAccess) {
ANativeWindow_Buffer buffer;
@ -2069,7 +2078,7 @@ AndroidBridge::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayRes
}
AndroidBridge::AndroidBridge()
: mLayerClient(NULL)
: mLayerClient(NULL)
{
}
@ -2167,7 +2176,7 @@ AndroidBridge::CreateSurface()
if (surface)
surface = env->NewGlobalRef(surface);
return surface;
#endif
}
@ -2198,9 +2207,9 @@ AndroidBridge::ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted
AutoLocalJNIFrame jniFrame(env, 0);
env->CallStaticVoidMethod(mGeckoAppShellClass, jShowSurface, surface,
(int)aRect.x, (int)aRect.y,
(int)aRect.width, (int)aRect.height,
aInverted, aBlend);
(int)aRect.x, (int)aRect.y,
(int)aRect.width, (int)aRect.height,
aInverted, aBlend);
#endif
}
@ -2309,10 +2318,10 @@ AndroidBridge::NotifyWakeLockChanged(const nsAString& topic, const nsAString& st
AutoLocalJNIFrame jniFrame(env);
jstring jstrTopic = env->NewString(nsPromiseFlatString(topic).get(),
topic.Length());
jstring jstrState = env->NewString(nsPromiseFlatString(state).get(),
state.Length());
jstring jstrTopic = NewJavaString(&jniFrame, nsPromiseFlatString(topic).get(),
topic.Length());
jstring jstrState = NewJavaString(&jniFrame, nsPromiseFlatString(state).get(),
state.Length());
env->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyWakeLockChanged, jstrTopic, jstrState);
}
@ -2373,7 +2382,7 @@ nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt
{
nsresult rv;
float scale = 1.0;
if (!buffer)
return NS_OK;
@ -2446,7 +2455,7 @@ nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt
context->Scale(scale * dstW / srcW, scale * dstH / srcH);
rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
NS_ENSURE_SUCCESS(rv, rv);
env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot,
env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot,
buffer, tabId, dstX, dstY, dstX + dstW, dstY + dstH, bufW, bufH, token);
return NS_OK;
}

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

@ -34,6 +34,7 @@
#endif
#include "mozilla/BlockingResourceBase.h"
#include "mozilla/mozPoisonWrite.h"
#ifdef HAVE_DLOPEN
#include <dlfcn.h>
@ -646,6 +647,7 @@ static bool InitLog(const char* envVar, const char* msg, FILE* *result)
}
stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
if (stream != NULL) {
MozillaRegisterDebugFD(fileno(stream));
*result = stream;
fprintf(stdout, "### %s defined -- logging %s to %s\n",
envVar, msg, fname.get());
@ -1235,6 +1237,17 @@ nsTraceRefcntImpl::Startup()
{
}
static void maybeUnregisterAndCloseFile(FILE *&f) {
if (!f)
return;
int fd = fileno(f);
fclose(f);
if (fd != 1 && fd != 2)
MozillaUnRegisterDebugFD(fd);
f = nsnull;
}
void
nsTraceRefcntImpl::Shutdown()
{
@ -1256,26 +1269,11 @@ nsTraceRefcntImpl::Shutdown()
PL_HashTableDestroy(gSerialNumbers);
gSerialNumbers = nsnull;
}
if (gBloatLog) {
fclose(gBloatLog);
gBloatLog = nsnull;
}
if (gRefcntsLog) {
fclose(gRefcntsLog);
gRefcntsLog = nsnull;
}
if (gAllocLog) {
fclose(gAllocLog);
gAllocLog = nsnull;
}
if (gLeakyLog) {
fclose(gLeakyLog);
gLeakyLog = nsnull;
}
if (gCOMPtrLog) {
fclose(gCOMPtrLog);
gCOMPtrLog = nsnull;
}
maybeUnregisterAndCloseFile(gBloatLog);
maybeUnregisterAndCloseFile(gRefcntsLog);
maybeUnregisterAndCloseFile(gAllocLog);
maybeUnregisterAndCloseFile(gLeakyLog);
maybeUnregisterAndCloseFile(gCOMPtrLog);
#endif
}

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

@ -27,6 +27,10 @@ ifeq ($(OS_ARCH),Linux)
DEFINES += -DXP_LINUX
endif
ifeq (Darwin, $(OS_ARCH))
CSRCS = mach_override.c
endif
CPPSRCS = \
$(XPCOM_GLUE_SRC_LCPPSRCS) \
$(XPCOM_GLUENS_SRC_LCPPSRCS) \
@ -37,6 +41,12 @@ CPPSRCS = \
FileLocation.cpp \
$(NULL)
ifeq (Darwin, $(OS_ARCH))
CPPSRCS += mozPoisonWriteMac.cpp
else
CPPSRCS += mozPoisonWriteStub.cpp
endif
ifeq ($(OS_ARCH),WINNT)
CPPSRCS += perfprobe.cpp
endif
@ -95,6 +105,7 @@ EXPORTS_mozilla = \
ServiceList.h \
Omnijar.h \
FileLocation.h \
mozPoisonWrite.h \
$(NULL)
ifeq ($(OS_ARCH),WINNT)

768
xpcom/build/mach_override.c Normal file
Просмотреть файл

@ -0,0 +1,768 @@
/* copied from https://github.com/rentzsch/mach_star */
/*******************************************************************************
mach_override.c
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
***************************************************************************/
#include "mach_override.h"
#include <mach-o/dyld.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <mach/vm_map.h>
#include <sys/mman.h>
#include <CoreServices/CoreServices.h>
/**************************
*
* Constants
*
**************************/
#pragma mark -
#pragma mark (Constants)
#if defined(__ppc__) || defined(__POWERPC__)
long kIslandTemplate[] = {
0x9001FFFC, // stw r0,-4(SP)
0x3C00DEAD, // lis r0,0xDEAD
0x6000BEEF, // ori r0,r0,0xBEEF
0x7C0903A6, // mtctr r0
0x8001FFFC, // lwz r0,-4(SP)
0x60000000, // nop ; optionally replaced
0x4E800420 // bctr
};
#define kAddressHi 3
#define kAddressLo 5
#define kInstructionHi 10
#define kInstructionLo 11
#elif defined(__i386__)
#define kOriginalInstructionsSize 16
char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
// Now the real jump instruction
0xE9, 0xEF, 0xBE, 0xAD, 0xDE
};
#define kInstructions 0
#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
#elif defined(__x86_64__)
#define kOriginalInstructionsSize 32
#define kJumpAddress kOriginalInstructionsSize + 6
char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
// Now the real jump instruction
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
#endif
#define kAllocateHigh 1
#define kAllocateNormal 0
/**************************
*
* Data Types
*
**************************/
#pragma mark -
#pragma mark (Data Types)
typedef struct {
char instructions[sizeof(kIslandTemplate)];
int allocatedHigh;
} BranchIsland;
/**************************
*
* Funky Protos
*
**************************/
#pragma mark -
#pragma mark (Funky Protos)
mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
void *originalFunctionAddress);
mach_error_t
freeBranchIsland(
BranchIsland *island );
#if defined(__ppc__) || defined(__POWERPC__)
mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
long instruction );
#endif
#if defined(__i386__) || defined(__x86_64__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions );
void
atomic_mov64(
uint64_t *targetAddress,
uint64_t value );
static Boolean
eatKnownInstructions(
unsigned char *code,
uint64_t *newInstruction,
int *howManyEaten,
char *originalInstructions,
int *originalInstructionCount,
uint8_t *originalInstructionSizes );
static void
fixupInstructions(
void *originalFunction,
void *escapeIsland,
void *instructionsToFix,
int instructionCount,
uint8_t *instructionSizes );
#endif
/*******************************************************************************
*
* Interface
*
*******************************************************************************/
#pragma mark -
#pragma mark (Interface)
#if defined(__i386__) || defined(__x86_64__)
mach_error_t makeIslandExecutable(void *address) {
mach_error_t err = err_none;
vm_size_t pageSize;
host_page_size( mach_host_self(), &pageSize );
uintptr_t page = (uintptr_t)address & ~(uintptr_t)(pageSize-1);
int e = err_none;
e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
e |= msync((void *)page, pageSize, MS_INVALIDATE );
if (e) {
err = err_cannot_override;
}
return err;
}
#endif
mach_error_t
mach_override_ptr(
void *originalFunctionAddress,
const void *overrideFunctionAddress,
void **originalFunctionReentryIsland )
{
assert( originalFunctionAddress );
assert( overrideFunctionAddress );
// this addresses overriding such functions as AudioOutputUnitStart()
// test with modified DefaultOutputUnit project
#if defined(__x86_64__)
for(;;){
if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
else break;
}
#elif defined(__i386__)
for(;;){
if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
else break;
}
#endif
long *originalFunctionPtr = (long*) originalFunctionAddress;
mach_error_t err = err_none;
#if defined(__ppc__) || defined(__POWERPC__)
// Ensure first instruction isn't 'mfctr'.
#define kMFCTRMask 0xfc1fffff
#define kMFCTRInstruction 0x7c0903a6
long originalInstruction = *originalFunctionPtr;
if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
err = err_cannot_override;
#elif defined(__i386__) || defined(__x86_64__)
int eatenCount = 0;
int originalInstructionCount = 0;
char originalInstructions[kOriginalInstructionsSize];
uint8_t originalInstructionSizes[kOriginalInstructionsSize];
uint64_t jumpRelativeInstruction = 0; // JMP
Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
&jumpRelativeInstruction, &eatenCount,
originalInstructions, &originalInstructionCount,
originalInstructionSizes );
if (eatenCount > kOriginalInstructionsSize) {
//printf ("Too many instructions eaten\n");
overridePossible = false;
}
if (!overridePossible) err = err_cannot_override;
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
#endif
// Make the original function implementation writable.
if( !err ) {
err = vm_protect( mach_task_self(),
(vm_address_t) originalFunctionPtr, 8, false,
(VM_PROT_ALL | VM_PROT_COPY) );
if( err )
err = vm_protect( mach_task_self(),
(vm_address_t) originalFunctionPtr, 8, false,
(VM_PROT_DEFAULT | VM_PROT_COPY) );
}
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
// Allocate and target the escape island to the overriding function.
BranchIsland *escapeIsland = NULL;
if( !err )
err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
#if defined(__ppc__) || defined(__POWERPC__)
if( !err )
err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
// Build the branch absolute instruction to the escape island.
long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
if( !err ) {
long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
}
#elif defined(__i386__) || defined(__x86_64__)
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
if( !err )
err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
// Build the jump relative instruction to the escape island
#endif
#if defined(__i386__) || defined(__x86_64__)
if (!err) {
uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
addressOffset = OSSwapInt32(addressOffset);
jumpRelativeInstruction |= 0xE900000000000000LL;
jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
}
#endif
// Optionally allocate & return the reentry island. This may contain relocated
// jmp instructions and so has all the same addressing reachability requirements
// the escape island has to the original function, except the escape island is
// technically our original function.
BranchIsland *reentryIsland = NULL;
if( !err && originalFunctionReentryIsland ) {
err = allocateBranchIsland( &reentryIsland, kAllocateHigh, escapeIsland);
if( !err )
*originalFunctionReentryIsland = reentryIsland;
}
#if defined(__ppc__) || defined(__POWERPC__)
// Atomically:
// o If the reentry island was allocated:
// o Insert the original instruction into the reentry island.
// o Target the reentry island at the 2nd instruction of the
// original function.
// o Replace the original instruction with the branch absolute.
if( !err ) {
int escapeIslandEngaged = false;
do {
if( reentryIsland )
err = setBranchIslandTarget( reentryIsland,
(void*) (originalFunctionPtr+1), originalInstruction );
if( !err ) {
escapeIslandEngaged = CompareAndSwap( originalInstruction,
branchAbsoluteInstruction,
(UInt32*)originalFunctionPtr );
if( !escapeIslandEngaged ) {
// Someone replaced the instruction out from under us,
// re-read the instruction, make sure it's still not
// 'mfctr' and try again.
originalInstruction = *originalFunctionPtr;
if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
err = err_cannot_override;
}
}
} while( !err && !escapeIslandEngaged );
}
#elif defined(__i386__) || defined(__x86_64__)
// Atomically:
// o If the reentry island was allocated:
// o Insert the original instructions into the reentry island.
// o Target the reentry island at the first non-replaced
// instruction of the original function.
// o Replace the original first instructions with the jump relative.
//
// Note that on i386, we do not support someone else changing the code under our feet
if ( !err ) {
fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions,
originalInstructionCount, originalInstructionSizes );
if( reentryIsland )
err = setBranchIslandTarget_i386( reentryIsland,
(void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
// try making islands executable before planting the jmp
#if defined(__x86_64__) || defined(__i386__)
if( !err )
err = makeIslandExecutable(escapeIsland);
if( !err && reentryIsland )
err = makeIslandExecutable(reentryIsland);
#endif
if ( !err )
atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
}
#endif
// Clean up on error.
if( err ) {
if( reentryIsland )
freeBranchIsland( reentryIsland );
if( escapeIsland )
freeBranchIsland( escapeIsland );
}
return err;
}
/*******************************************************************************
*
* Implementation
*
*******************************************************************************/
#pragma mark -
#pragma mark (Implementation)
/***************************************************************************//**
Implementation: Allocates memory for a branch island.
@param island <- The allocated island.
@param allocateHigh -> Whether to allocate the island at the end of the
address space (for use with the branch absolute
instruction).
@result <- mach_error_t
***************************************************************************/
mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
void *originalFunctionAddress)
{
assert( island );
mach_error_t err = err_none;
if( allocateHigh ) {
vm_size_t pageSize;
err = host_page_size( mach_host_self(), &pageSize );
if( !err ) {
assert( sizeof( BranchIsland ) <= pageSize );
#if defined(__ppc__) || defined(__POWERPC__)
vm_address_t first = 0xfeffffff;
vm_address_t last = 0xfe000000 + pageSize;
#elif defined(__x86_64__)
vm_address_t first = (uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1) | ((uint64_t)1 << 31); // start in the middle of the page?
vm_address_t last = 0x0;
#else
vm_address_t first = 0xffc00000;
vm_address_t last = 0xfffe0000;
#endif
vm_address_t page = first;
int allocated = 0;
vm_map_t task_self = mach_task_self();
while( !err && !allocated && page != last ) {
err = vm_allocate( task_self, &page, pageSize, 0 );
if( err == err_none )
allocated = 1;
else if( err == KERN_NO_SPACE ) {
#if defined(__x86_64__)
page -= pageSize;
#else
page += pageSize;
#endif
err = err_none;
}
}
if( allocated )
*island = (BranchIsland*) page;
else if( !allocated && !err )
err = KERN_NO_SPACE;
}
} else {
void *block = malloc( sizeof( BranchIsland ) );
if( block )
*island = block;
else
err = KERN_NO_SPACE;
}
if( !err )
(**island).allocatedHigh = allocateHigh;
return err;
}
/***************************************************************************//**
Implementation: Deallocates memory for a branch island.
@param island -> The island to deallocate.
@result <- mach_error_t
***************************************************************************/
mach_error_t
freeBranchIsland(
BranchIsland *island )
{
assert( island );
assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
assert( island->allocatedHigh );
mach_error_t err = err_none;
if( island->allocatedHigh ) {
vm_size_t pageSize;
err = host_page_size( mach_host_self(), &pageSize );
if( !err ) {
assert( sizeof( BranchIsland ) <= pageSize );
err = vm_deallocate(
mach_task_self(),
(vm_address_t) island, pageSize );
}
} else {
free( island );
}
return err;
}
/***************************************************************************//**
Implementation: Sets the branch island's target, with an optional
instruction.
@param island -> The branch island to insert target into.
@param branchTo -> The address of the target.
@param instruction -> Optional instruction to execute prior to branch. Set
to zero for nop.
@result <- mach_error_t
***************************************************************************/
#if defined(__ppc__) || defined(__POWERPC__)
mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
long instruction )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// Fill in the address.
((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
((short*)island->instructions)[kAddressHi]
= (((long) branchTo) >> 16) & 0x0000FFFF;
// Fill in the (optional) instuction.
if( instruction != 0 ) {
((short*)island->instructions)[kInstructionLo]
= instruction & 0x0000FFFF;
((short*)island->instructions)[kInstructionHi]
= (instruction >> 16) & 0x0000FFFF;
}
//MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#endif
#if defined(__i386__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// copy original instructions
if (instructions) {
bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
}
// Fill in the address.
int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
*((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#elif defined(__x86_64__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// Copy original instructions.
if (instructions) {
bcopy (instructions, island->instructions, kOriginalInstructionsSize);
}
// Fill in the address.
*((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#endif
#if defined(__i386__) || defined(__x86_64__)
// simplistic instruction matching
typedef struct {
unsigned int length; // max 15
unsigned char mask[15]; // sequence of bytes in memory order
unsigned char constraint[15]; // sequence of bytes in memory order
} AsmInstructionMatch;
#if defined(__i386__)
static AsmInstructionMatch possibleInstructions[] = {
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
{ 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret
{ 0x1, {0xFF}, {0x90} }, // nop
{ 0x1, {0xFF}, {0x55} }, // push %esp
{ 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
{ 0x1, {0xFF}, {0x53} }, // push %ebx
{ 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
{ 0x1, {0xFF}, {0x57} }, // push %edi
{ 0x1, {0xFF}, {0x56} }, // push %esi
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
{ 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
{ 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
{ 0x0 }
};
#elif defined(__x86_64__)
static AsmInstructionMatch possibleInstructions[] = {
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
{ 0x1, {0xFF}, {0x90} }, // nop
{ 0x1, {0xF8}, {0x50} }, // push %rX
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
{ 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
{ 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
{ 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
{ 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
{ 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
{ 0x0 }
};
#endif
static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
{
Boolean match = true;
size_t i;
for (i=0; i<instruction->length; i++) {
unsigned char mask = instruction->mask[i];
unsigned char constraint = instruction->constraint[i];
unsigned char codeValue = code[i];
match = ((codeValue & mask) == constraint);
if (!match) break;
}
return match;
}
#if defined(__i386__) || defined(__x86_64__)
static Boolean
eatKnownInstructions(
unsigned char *code,
uint64_t *newInstruction,
int *howManyEaten,
char *originalInstructions,
int *originalInstructionCount,
uint8_t *originalInstructionSizes )
{
Boolean allInstructionsKnown = true;
int totalEaten = 0;
unsigned char* ptr = code;
int remainsToEat = 5; // a JMP instruction takes 5 bytes
int instructionIndex = 0;
if (howManyEaten) *howManyEaten = 0;
if (originalInstructionCount) *originalInstructionCount = 0;
while (remainsToEat > 0) {
Boolean curInstructionKnown = false;
// See if instruction matches one we know
AsmInstructionMatch* curInstr = possibleInstructions;
do {
if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
curInstr++;
} while (curInstr->length > 0);
// if all instruction matches failed, we don't know current instruction then, stop here
if (!curInstructionKnown) {
allInstructionsKnown = false;
fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
break;
}
// At this point, we've matched curInstr
int eaten = curInstr->length;
ptr += eaten;
remainsToEat -= eaten;
totalEaten += eaten;
if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
instructionIndex += 1;
if (originalInstructionCount) *originalInstructionCount = instructionIndex;
}
if (howManyEaten) *howManyEaten = totalEaten;
if (originalInstructions) {
Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
if (enoughSpaceForOriginalInstructions) {
memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
bcopy(code, originalInstructions, totalEaten);
} else {
// printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
return false;
}
}
if (allInstructionsKnown) {
// save last 3 bytes of first 64bits of codre we'll replace
uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
// keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
*newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
*newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
}
return allInstructionsKnown;
}
static void
fixupInstructions(
void *originalFunction,
void *escapeIsland,
void *instructionsToFix,
int instructionCount,
uint8_t *instructionSizes )
{
int index;
for (index = 0;index < instructionCount;index += 1)
{
if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
{
uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland;
uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
*jumpOffsetPtr += offset;
}
originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]);
escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]);
instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
}
}
#endif
#if defined(__i386__)
__asm(
".text;"
".align 2, 0x90;"
"_atomic_mov64:;"
" pushl %ebp;"
" movl %esp, %ebp;"
" pushl %esi;"
" pushl %ebx;"
" pushl %ecx;"
" pushl %eax;"
" pushl %edx;"
// atomic push of value to an address
// we use cmpxchg8b, which compares content of an address with
// edx:eax. If they are equal, it atomically puts 64bit value
// ecx:ebx in address.
// We thus put contents of address in edx:eax to force ecx:ebx
// in address
" mov 8(%ebp), %esi;" // esi contains target address
" mov 12(%ebp), %ebx;"
" mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
" mov (%esi), %eax;"
" mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
" lock; cmpxchg8b (%esi);" // atomic move.
// restore registers
" popl %edx;"
" popl %eax;"
" popl %ecx;"
" popl %ebx;"
" popl %esi;"
" popl %ebp;"
" ret"
);
#elif defined(__x86_64__)
void atomic_mov64(
uint64_t *targetAddress,
uint64_t value )
{
*targetAddress = value;
}
#endif
#endif

121
xpcom/build/mach_override.h Normal file
Просмотреть файл

@ -0,0 +1,121 @@
/*******************************************************************************
mach_override.h
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
***************************************************************************/
/***************************************************************************//**
@mainpage mach_override
@author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
This package, coded in C to the Mach API, allows you to override ("patch")
program- and system-supplied functions at runtime. You can fully replace
functions with your implementations, or merely head- or tail-patch the
original implementations.
Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
@todo Discontinue use of Carbon's MakeDataExecutable() and
CompareAndSwap() calls and start using the Mach equivalents, if they
exist. If they don't, write them and roll them in. That way, this
code will be pure Mach, which will make it easier to use everywhere.
Update: MakeDataExecutable() has been replaced by
msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
I'm currently unsure if I can link against it. May have to roll in
my own version...
@todo Stop using an entire 4K high-allocated VM page per 28-byte escape
branch island. Done right, this will dramatically speed up escape
island allocations when they number over 250. Then again, if you're
overriding more than 250 functions, maybe speed isn't your main
concern...
@todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
first-instructions. Initially, we should refuse to override
functions beginning with these instructions. Eventually, we should
dynamically rewrite them to make them position-independent.
@todo Write mach_unoverride(), which would remove an override placed on a
function. Must be multiple-override aware, which means an almost
complete rewrite under the covers, because the target address can't
be spread across two load instructions like it is now since it will
need to be atomically updatable.
@todo Add non-rentry variants of overrides to test_mach_override.
***************************************************************************/
#ifndef _mach_override_
#define _mach_override_
#include <sys/types.h>
#include <mach/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Returned if the function to be overrided begins with a 'mfctr' instruction.
*/
#define err_cannot_override (err_local|1)
/************************************************************************************//**
Dynamically overrides the function implementation referenced by
originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
the original implementation.
@param originalFunctionAddress -> Required address of the function to
override (with overrideFunctionAddress).
@param overrideFunctionAddress -> Required address to the overriding
function.
@param originalFunctionReentryIsland <- Optional pointer to pointer to the
reentry island. Can be NULL.
@result <- err_cannot_override if the original
function's implementation begins with
the 'mfctr' instruction.
************************************************************************************/
mach_error_t
mach_override_ptr(
void *originalFunctionAddress,
const void *overrideFunctionAddress,
void **originalFunctionReentryIsland );
/************************************************************************************//**
************************************************************************************/
#ifdef __cplusplus
#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
{ \
static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
public: \
static kern_return_t override(void *originalFunctionPtr) { \
kern_return_t result = err_none; \
if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
ORIGINAL_FUNCTION_NAME##_overriden = true; \
result = mach_override_ptr( (void*)originalFunctionPtr, \
(void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
(void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
} \
return result; \
} \
static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
} \
}; \
\
err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
}
#endif
#ifdef __cplusplus
}
#endif
#endif // _mach_override_

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

@ -0,0 +1,19 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 "mozilla/Types.h"
MOZ_BEGIN_EXTERN_C
void MozillaRegisterDebugFD(int fd);
void MozillaUnRegisterDebugFD(int fd);
MOZ_END_EXTERN_C
#ifdef __cplusplus
namespace mozilla {
void PoisonWrite();
void DisableWritePoisoning();
}
#endif

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

@ -0,0 +1,257 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 "mozilla/mozPoisonWrite.h"
#include "mozilla/Util.h"
#include "nsTraceRefcntImpl.h"
#include "mozilla/Assertions.h"
#include "mozilla/Scoped.h"
#include "mozilla/Mutex.h"
#include "nsStackWalk.h"
#include "mach_override.h"
#include <sys/stat.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <sys/uio.h>
#include <aio.h>
#include <dlfcn.h>
namespace {
using namespace mozilla;
struct FuncData {
const char *Name; // Name of the function for the ones we use dlsym
const void *Wrapper; // The function that we will replace 'Function' with
void *Function; // The function that will be replaced with 'Wrapper'
void *Buffer; // Will point to the jump buffer that lets us call
// 'Function' after it has been replaced.
};
typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
void AbortOnBadWrite(int fd, const void *wbuf, size_t count);
template<FuncData &foo>
ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
AbortOnBadWrite(fd, buf, count);
write_t old_write = (write_t) foo.Buffer;
return old_write(fd, buf, count);
}
// FIXME: clang accepts usinging wrap_data_temp<X ## _data> in the struct
// initialization. Is this a gcc 4.2 bug?
// Define a FuncData for a function that can be found without dlsym.
#define DEFINE_F_DATA(X) \
ssize_t wrap_ ## X (int fd, const void *buf, size_t count); \
FuncData X ## _data = { 0, (void*) wrap_ ## X, (void*) X }; \
ssize_t wrap_ ## X (int fd, const void *buf, size_t count) { \
return wrap_write_temp<X ## _data>(fd, buf, count); \
}
// Define a FuncData for a function that can only be found with dlsym.
#define DEFINE_F_DATA_DYN(X, NAME) \
ssize_t wrap_ ## X (int fd, const void *buf, size_t count); \
FuncData X ## _data = { NAME, (void*) wrap_ ## X }; \
ssize_t wrap_ ## X (int fd, const void *buf, size_t count) { \
return wrap_write_temp<X ## _data>(fd, buf, count); \
}
// Define a simple FuncData that just aborts.
#define DEFINE_F_DATA_ABORT(X) \
void wrap_ ## X() { abort(); } \
FuncData X ## _data = { 0, (void*) wrap_ ## X, (void*) X }
// Define a simple FuncData that just aborts for a function that needs dlsym.
#define DEFINE_F_DATA_ABORT_DYN(X, NAME) \
void wrap_ ## X() { abort(); } \
FuncData X ## _data = { NAME, (void*) wrap_ ## X }
DEFINE_F_DATA_ABORT(aio_write);
DEFINE_F_DATA_ABORT(pwrite);
// These exist on 32 bit OS X
DEFINE_F_DATA_ABORT_DYN(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
DEFINE_F_DATA_ABORT_DYN(writev_UNIX2003, "writev$UNIX2003");
DEFINE_F_DATA_ABORT_DYN(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
DEFINE_F_DATA_ABORT_DYN(pwrite_UNIX2003, "pwrite$UNIX2003");
// These exist on 64 bit OS X
DEFINE_F_DATA_ABORT_DYN(writev_NOCANCEL, "writev$NOCANCEL");
DEFINE_F_DATA_ABORT_DYN(pwrite_NOCANCEL, "pwrite$NOCANCEL");
DEFINE_F_DATA(write);
typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
ssize_t wrap_writev(int fd, const struct iovec *iov, int iovcnt);
FuncData writev_data = { 0, (void*) wrap_writev, (void*) writev };
ssize_t wrap_writev(int fd, const struct iovec *iov, int iovcnt) {
AbortOnBadWrite(fd, 0, iovcnt);
writev_t old_write = (writev_t) writev_data.Buffer;
return old_write(fd, iov, iovcnt);
}
// These exist on 32 bit OS X
DEFINE_F_DATA_DYN(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
DEFINE_F_DATA_DYN(write_UNIX2003, "write$UNIX2003");
// These exist on 64 bit OS X
DEFINE_F_DATA_DYN(write_NOCANCEL, "write$NOCANCEL");
FuncData *Functions[] = { &aio_write_data,
&pwrite_data,
&pwrite_NOCANCEL_UNIX2003_data,
&pwrite_UNIX2003_data,
&pwrite_NOCANCEL_data,
&write_data,
&write_NOCANCEL_UNIX2003_data,
&write_UNIX2003_data,
&write_NOCANCEL_data,
&writev_data,
&writev_NOCANCEL_UNIX2003_data,
&writev_UNIX2003_data,
&writev_NOCANCEL_data};
const int NumFunctions = ArrayLength(Functions);
std::vector<int>& getDebugFDs() {
// We have to use new as some write happen during static destructors
// so an static std::vector might be destroyed while we still need it.
static std::vector<int> *DebugFDs = new std::vector<int>();
return *DebugFDs;
}
struct AutoLockTraits {
typedef PRLock *type;
const static type empty() {
return NULL;
}
const static void release(type aL) {
PR_Unlock(aL);
}
};
class MyAutoLock : public Scoped<AutoLockTraits> {
public:
static PRLock *getDebugFDsLock() {
// We have to use something lower level than a mutex. If we don't, we
// can get recursive in here when called from logging a call to free.
static PRLock *Lock = PR_NewLock();
return Lock;
}
MyAutoLock() : Scoped<AutoLockTraits>(getDebugFDsLock()) {
PR_Lock(get());
}
};
bool PoisoningDisabled = false;
void AbortOnBadWrite(int fd, const void *wbuf, size_t count) {
if (PoisoningDisabled)
return;
// Ignore writes of zero bytes, firefox does some during shutdown.
if (count == 0)
return;
// Stdout and Stderr are OK.
if(fd == 1 || fd == 2)
return;
struct stat buf;
int rv = fstat(fd, &buf);
MOZ_ASSERT(rv == 0);
// FIFOs are used for thread communication during shutdown.
if ((buf.st_mode & S_IFMT) == S_IFIFO)
return;
MyAutoLock lockedScope;
// Debugging FDs are OK
std::vector<int> &Vec = getDebugFDs();
if (std::find(Vec.begin(), Vec.end(), fd) != Vec.end())
return;
// For writev we pass NULL in wbuf. We should only get here from
// dbm, and it uses write, so assert that we have wbuf.
MOZ_ASSERT(wbuf);
// As a really bad hack, accept writes that don't change the on disk
// content. This is needed because dbm doesn't keep track of dirty bits
// and can end up writing the same data to disk twice. Once when the
// user (nss) asks it to sync and once when closing the database.
void *wbuf2 = malloc(count);
MOZ_ASSERT(wbuf2);
off_t pos = lseek(fd, 0, SEEK_CUR);
MOZ_ASSERT(pos != -1);
ssize_t r = read(fd, wbuf2, count);
MOZ_ASSERT(r == count);
int cmp = memcmp(wbuf, wbuf2, count);
MOZ_ASSERT(cmp == 0);
free(wbuf2);
off_t pos2 = lseek(fd, pos, SEEK_SET);
MOZ_ASSERT(pos2 == pos);
}
// We cannot use destructors to free the lock and the list of debug fds since
// we don't control the order the destructors are called. Instead, we use
// libc funcion __cleanup callback which runs after the destructors.
void (*OldCleanup)();
extern "C" void (*__cleanup)();
void FinalCleanup() {
if (OldCleanup)
OldCleanup();
delete &getDebugFDs();
PR_DestroyLock(MyAutoLock::getDebugFDsLock());
}
} // anonymous namespace
extern "C" {
void MozillaRegisterDebugFD(int fd) {
MyAutoLock lockedScope;
std::vector<int> &Vec = getDebugFDs();
MOZ_ASSERT(std::find(Vec.begin(), Vec.end(), fd) == Vec.end());
Vec.push_back(fd);
}
void MozillaUnRegisterDebugFD(int fd) {
MyAutoLock lockedScope;
std::vector<int> &Vec = getDebugFDs();
std::vector<int>::iterator i = std::find(Vec.begin(), Vec.end(), fd);
MOZ_ASSERT(i != Vec.end());
Vec.erase(i);
}
}
namespace mozilla {
void PoisonWrite() {
// For now only poison writes in debug builds.
#ifndef DEBUG
return;
#endif
PoisoningDisabled = false;
OldCleanup = __cleanup;
__cleanup = FinalCleanup;
for (int i = 0; i < NumFunctions; ++i) {
FuncData *d = Functions[i];
if (!d->Function)
d->Function = dlsym(RTLD_DEFAULT, d->Name);
if (!d->Function)
continue;
mach_error_t t = mach_override_ptr(d->Function, d->Wrapper,
&d->Buffer);
MOZ_ASSERT(t == err_none);
}
}
void DisableWritePoisoning() {
PoisoningDisabled = true;
}
}

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

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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/. */
namespace mozilla {
void PoisonWrite() {
}
void DisableWritePoisoning() {
}
}
extern "C" {
void MozillaRegisterDebugFD(int fd) {
}
void MozillaUnRegisterDebugFD(int fd) {
}
}

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

@ -106,6 +106,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
#include "nsChromeRegistry.h"
#include "nsChromeProtocolHandler.h"
#include "mozilla/mozPoisonWrite.h"
#include "mozilla/scache/StartupCache.h"
@ -647,6 +648,8 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
nsCycleCollector_shutdown();
mozilla::PoisonWrite();
if (moduleLoaders) {
bool more;
nsCOMPtr<nsISupports> el;