зеркало из https://github.com/mozilla/gecko-dev.git
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
Коммит
8471850074
|
@ -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)
|
||||
|
|
40
configure.in
40
configure.in
|
@ -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>♔♕♖♗♘♙</p>
|
||||
<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>
|
||||
<!--
|
||||
-- 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>♔♕♖♗♘♙</p>
|
||||
<p><b>♔♕♖♗♘♙</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)
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче