This commit is contained in:
Ryan VanderMeulen 2014-01-24 17:10:15 -05:00
Родитель b227db5f9b 07eebca28f
Коммит 1e0482e1c1
32 изменённых файлов: 947 добавлений и 132 удалений

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

@ -30,6 +30,10 @@ function LOG(s) {
dump("== Payment flow == " + s + "\n");
}
function LOGE(s) {
dump("== Payment flow ERROR == " + s + "\n");
}
if (_debug) {
LOG("Frame script injected");
}
@ -43,6 +47,12 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"nsIUUIDGenerator");
#ifdef MOZ_B2G_RIL
Cu.import('resource://gre/modules/ObjectWrapper.jsm');
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
"@mozilla.org/ril/content-helper;1",
"nsIIccProvider");
@ -59,6 +69,7 @@ const kSilentSmsReceivedTopic = "silent-sms-received";
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
const kRilDefaultDataServiceId = "ril.data.defaultServiceId";
const kRilDefaultPaymentServiceId = "ril.payment.defaultServiceId";
const MOBILEMESSAGECALLBACK_CID =
Components.ID("{b484d8c9-6be4-4f94-ab60-c9c7ebcc853d}");
@ -71,8 +82,8 @@ function SilentSmsRequest() {
SilentSmsRequest.prototype = {
__exposedProps__: {
onsuccess: 'rw',
onerror: 'rw'
onsuccess: "rw",
onerror: "rw"
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
@ -95,33 +106,69 @@ SilentSmsRequest.prototype = {
},
notifySendMessageFailed: function notifySendMessageFailed(aError) {
if (_debug) {
LOG("Error sending silent message " + aError);
}
LOGE("Error sending silent message " + aError);
this._onerror(aError);
}
};
function PaymentSettings() {
this.dataServiceId = 0;
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
gSettingsService.createLock().get(kRilDefaultDataServiceId, this);
[kRilDefaultDataServiceId, kRilDefaultPaymentServiceId].forEach(setting => {
gSettingsService.createLock().get(setting, this);
});
}
PaymentSettings.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISettingsServiceCallback,
Ci.nsIObserver]),
dataServiceId: 0,
_paymentServiceId: 0,
get paymentServiceId() {
return this._paymentServiceId;
},
set paymentServiceId(serviceId) {
// We allow the payment provider to set the service ID that will be used
// for the payment process.
// This service ID will be the one used by the silent SMS flow.
// If the payment is done with an external SIM, the service ID must be set
// to null.
if (serviceId != null && serviceId >= gRil.numRadioInterfaces) {
LOGE("Invalid service ID " + serviceId);
return;
}
gSettingsService.createLock().set(kRilDefaultPaymentServiceId,
serviceId, null);
this._paymentServiceId = serviceId;
},
setServiceId: function(aName, aValue) {
switch (aName) {
case kRilDefaultDataServiceId:
this.dataServiceId = aValue;
if (_debug) {
LOG("dataServiceId " + this.dataServiceId);
}
break;
case kRilDefaultPaymentServiceId:
this._paymentServiceId = aValue;
if (_debug) {
LOG("paymentServiceId " + this._paymentServiceId);
}
break;
}
},
handle: function(aName, aValue) {
if (aName != kRilDefaultDataServiceId) {
return;
}
this.dataServiceId = aValue;
if (_debug) {
LOG("dataServiceId " + this.dataServiceId);
}
this.setServiceId(aName, aValue);
},
observe: function(aSubject, aTopic, aData) {
@ -131,21 +178,15 @@ PaymentSettings.prototype = {
try {
let setting = JSON.parse(aData);
if (!setting.key || setting.key !== kRilDefaultDataServiceId) {
if (!setting.key ||
(setting.key !== kRilDefaultDataServiceId &&
setting.key !== kRilDefaultPaymentServiceId)) {
return;
}
this.dataServiceId = setting.value;
if (_debug) {
LOG("dataServiceId " + setting.value);
}
this.setServiceId(setting.key, setting.value);
} catch (e) {
if (_debug) {
LOG(e);
}
LOGE(e);
}
},
cleanup: function() {
@ -163,19 +204,18 @@ let gBrowser = Services.wm.getMostRecentWindow("navigator:browser");
let PaymentProvider = {
#ifdef MOZ_B2G_RIL
__exposedProps__: {
paymentSuccess: 'r',
paymentFailed: 'r',
iccIds: 'r',
mcc: 'r',
mnc: 'r',
sendSilentSms: 'r',
observeSilentSms: 'r',
removeSilentSmsObserver: 'r'
paymentSuccess: "r",
paymentFailed: "r",
paymentServiceId: "rw",
iccInfo: "r",
sendSilentSms: "r",
observeSilentSms: "r",
removeSilentSmsObserver: "r"
},
#else
__exposedProps__: {
paymentSuccess: 'r',
paymentFailed: 'r'
paymentSuccess: "r",
paymentFailed: "r"
},
#endif
@ -242,9 +282,7 @@ let PaymentProvider = {
},
paymentFailed: function paymentFailed(aErrorMsg) {
if (_debug) {
LOG("paymentFailed " + aErrorMsg);
}
LOGE("paymentFailed " + aErrorMsg);
PaymentProvider._closePaymentFlowDialog(function notifyError() {
if (!gRequestId) {
@ -256,22 +294,45 @@ let PaymentProvider = {
},
#ifdef MOZ_B2G_RIL
// Bug 938993. Support Multi-SIM for Payments.
get paymentServiceId() {
return this._settings.paymentServiceId;
},
set paymentServiceId(serviceId) {
this._settings.paymentServiceId = serviceId;
},
// We expose to the payment provider the information of all the SIMs
// available in the device. iccInfo is an object of this form:
// {
// "serviceId1": {
// mcc: <string>,
// mnc: <string>,
// iccId: <string>,
// dataPrimary: <boolean>
// },
// "serviceIdN": {...}
// }
get iccInfo() {
delete this.iccInfo;
return this.iccInfo = iccProvider.getIccInfo(this._settings.dataServiceId);
},
if (!this._iccInfo) {
this._iccInfo = {};
for (let i = 0; i < gRil.numRadioInterfaces; i++) {
let info = iccProvider.getIccInfo(i);
if (!info) {
LOGE("Tried to get the ICC info for an invalid service ID " + i);
continue;
}
get iccIds() {
return [this.iccInfo.iccid];
},
this._iccInfo[i] = {
iccId: info.iccid,
mcc: info.mcc,
mnc: info.mnc,
dataPrimary: i == this._settings.dataServiceId
};
}
}
get mcc() {
return [this.iccInfo.mcc];
},
get mnc() {
return [this.iccInfo.mnc];
return ObjectWrapper.wrap(this._iccInfo, content);
},
_silentNumbers: null,
@ -283,7 +344,21 @@ let PaymentProvider = {
}
let request = new SilentSmsRequest();
smsService.send(aNumber, aMessage, true, request);
if (this._settings.paymentServiceId === null) {
LOGE("No payment service ID set. Cannot send silent SMS");
let runnable = {
run: function run() {
request.notifySendMessageFailed("NO_PAYMENT_SERVICE_ID");
}
};
Services.tm.currentThread.dispatch(runnable,
Ci.nsIThread.DISPATCH_NORMAL);
return request;
}
smsService.send(this._settings.paymentServiceId, aNumber, aMessage, true,
request);
return request;
},
@ -349,6 +424,21 @@ let PaymentProvider = {
return;
}
// If the service ID is null it means that the payment provider asked the
// user for her MSISDN, so we are in a MT only SMS auth flow. In this case
// we manually set the service ID to the one corresponding with the SIM
// that received the SMS.
if (this._settings.paymentServiceId === null) {
let i = 0;
while(i < gRil.numRadioInterfaces) {
if (this.iccInfo[i].iccId === aSubject.iccId) {
this._settings.paymentServiceId = i;
break;
}
i++;
}
}
this._silentSmsObservers[number].forEach(function(callback) {
callback(aSubject);
});

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

@ -0,0 +1,40 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
const { Services } = Cu.import('resource://gre/modules/Services.jsm');
var processId;
function peekChildId(aSubject, aTopic, aData) {
Services.obs.removeObserver(peekChildId, 'recording-device-events');
Services.obs.removeObserver(peekChildId, 'recording-device-ipc-events');
let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
if (props.hasKey('childID')) {
processId = props.get('childID');
}
}
addMessageListener('init-chrome-event', function(message) {
// listen mozChromeEvent and forward to content process.
let browser = Services.wm.getMostRecentWindow('navigator:browser');
let type = message.type;
browser.addEventListener('mozChromeEvent', function(event) {
let details = event.detail;
if (details.type === type) {
sendAsyncMessage('chrome-event', details);
}
}, true);
Services.obs.addObserver(peekChildId, 'recording-device-events', false);
Services.obs.addObserver(peekChildId, 'recording-device-ipc-events', false);
});
addMessageListener('fake-content-shutdown', function(message) {
let props = Cc["@mozilla.org/hash-property-bag;1"]
.createInstance(Ci.nsIWritablePropertyBag2);
if (processId) {
props.setPropertyAsUint64('childID', processId);
}
Services.obs.notifyObservers(props, 'recording-device-ipc-events', 'content-shutdown');
});

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

@ -0,0 +1,82 @@
'use strict';
// resolve multiple promise in parallel
function expectAll(aValue) {
let deferred = new Promise(function(resolve, reject) {
let countdown = aValue.length;
let resolutionValues = new Array(countdown);
for (let i = 0; i < aValue.length; i++) {
let index = i;
aValue[i].then(function(val) {
resolutionValues[index] = val;
if (--countdown === 0) {
resolve(resolutionValues);
}
}, reject);
}
});
return deferred;
}
function TestInit() {
let url = SimpleTest.getTestFileURL("RecordingStatusChromeScript.js")
let script = SpecialPowers.loadChromeScript(url);
let helper = {
finish: function () {
script.destroy();
},
fakeShutdown: function () {
script.sendAsyncMessage('fake-content-shutdown', {});
}
};
script.addMessageListener('chrome-event', function (message) {
if (helper.hasOwnProperty('onEvent')) {
helper.onEvent(message);
} else {
ok(false, 'unexpected message: ' + JSON.stringify(message));
}
});
script.sendAsyncMessage("init-chrome-event", {
type: 'recording-status'
});
return Promise.resolve(helper);
}
function expectEvent(expected, eventHelper) {
return new Promise(function(resolve, reject) {
eventHelper.onEvent = function(message) {
delete eventHelper.onEvent;
ok(message, JSON.stringify(message));
is(message.type, 'recording-status', 'event type: ' + message.type);
is(message.active, expected.active, 'recording active: ' + message.active);
is(message.isAudio, expected.isAudio, 'audio recording active: ' + message.isAudio);
is(message.isVideo, expected.isVideo, 'video recording active: ' + message.isVideo);
resolve(eventHelper);
};
info('waiting for recording-status');
});
}
function expectStream(params, callback) {
return new Promise(function(resolve, reject) {
var req = navigator.mozGetUserMedia(
params,
function(stream) {
ok(true, 'create media stream');
callback(stream);
resolve();
},
function(err) {
ok(false, 'fail to create media stream');
reject(err);
}
);
info('waiting for gUM result');
});
}

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

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Iframe for Recording Status</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.7" src="RecordingStatusHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">
var localStream;
window.addEventListener('message', function(event) {
switch (event.data) {
case 'start':
let gumDeferred = expectStream({ audio: true,
fake: true
}, function(stream) {
localStream = stream;
event.source.postMessage('start-finished', window.location.origin);
});
break;
case 'stop':
localStream.stop();
localStream = null;
break;
}
}, false);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,10 @@
[DEFAULT]
support-files =
RecordingStatusChromeScript.js
RecordingStatusHelper.js
file_getusermedia_iframe.html
[test_recordingStatus_basic.html]
[test_recordingStatus_multiple_requests.html]
[test_recordingStatus_iframe.html]
[test_recordingStatus_kill_content_process.html]

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

@ -0,0 +1,7 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
MOCHITEST_MANIFESTS += ['mochitest.ini']

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

@ -0,0 +1,121 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Recording Status</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.7" src="RecordingStatusHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">
'use strict';
SimpleTest.waitForExplicitFinish();
function test() {
let localStreams = [];
TestInit().then(function(eventHelper) {
/* step 1: create one audio stream
* expect: see one mozChromeEvent for audio recording start.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: false
}, eventHelper);
let gumDeferred = expectStream({ audio: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 2: close the audio stream
* expect: see one mozChromeEvent for recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false,
}, eventHelper);
localStreams.shift().stop();
info('stop audio stream');
return eventDeferred;
}).then(function(eventHelper) {
/* step 3: create one video stream
* expect: see one mozChromeEvent for video recording start
*/
let eventDeferred = expectEvent({ active: true,
isAudio: false,
isVideo: true
}, eventHelper);
let gumDeferred = expectStream({ video: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 4: close the audio stream
* expect: see one mozChromeEvent for recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false,
}, eventHelper);
localStreams.shift().stop();
info('stop video stream');
return eventDeferred;
}).then(function(eventHelper) {
/* step 3: create one audio/video stream
* expect: see one mozChromeEvent for audio/video recording start
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: true
}, eventHelper);
let gumDeferred = expectStream({ audio: true,
video: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 4: close the audio stream
* expect: see one mozChromeEvent for recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false,
}, eventHelper);
localStreams.shift().stop();
info('stop audio/video stream');
return eventDeferred;
}).then(function(eventHelper) {
eventHelper.finish();
SimpleTest.finish();
});
}
// ensure Promise API is enabled
SpecialPowers.pushPrefEnv({
"set": [
["dom.promise.enabled", true],
['media.navigator.permission.disabled', true]
]
}, test);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Recording Status in iframe</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.7" src="RecordingStatusHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<iframe id="gum-iframe"></iframe>
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
function test() {
TestInit().then(function(eventHelper) {
/* step 1: load iframe whilch creates audio stream
* expect: see one mozChromeEvent for audio recording start.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: false
}, eventHelper);
let loadDeferred = new Promise(function(resolve, reject) {
let gumIframe = document.getElementById('gum-iframe');
gumIframe.src = 'file_getusermedia_iframe.html';
window.addEventListener('message', function(event) {
if (event.data === 'start-finished') {
resolve();
}
}, false);
gumIframe.onload = function() {
info('start audio stream in iframe');
gumIframe.contentWindow.postMessage('start', window.location.origin);
};
});
return expectAll([eventDeferred, loadDeferred]);
}).then(function([eventHelper]) {
/* step 2: close the audio stream
* expect: see one mozChromeEvent for recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false
}, eventHelper);
let win = document.getElementById('gum-iframe').contentWindow;
win.postMessage('stop', window.location.origin);
info('stop audio stream in iframe');
return eventDeferred;
}).then(function(eventHelper) {
eventHelper.finish();
SimpleTest.finish();
});
}
// ensure Promise API is enabled
SpecialPowers.pushPrefEnv({
"set": [
["dom.promise.enabled", true],
['media.navigator.permission.disabled', true]
]
}, test);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Recording Status after process shutdown</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.7" src="RecordingStatusHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
function test() {
let localStreams = [];
TestInit().then(function(eventHelper) {
/* step 1: load iframe whilch creates audio stream
* expect: see one mozChromeEvent for audio recording start.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: false
}, eventHelper);
let gumDeferred = expectStream({ audio: true,
fake: true
}, function(stream) { localStreams.push(stream); });
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 2: create video stream
* expect: see one mozChromeEvent for audio recording start.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: true
}, eventHelper);
let gumDeferred = expectStream({ video: true,
fake: true
}, function(stream) { localStreams.push(stream); });
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 3: close the audio stream
* expect: see one mozChromeEvent for recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false
}, eventHelper);
eventHelper.fakeShutdown();
info('simulate content process been killed');
return eventDeferred;
}).then(function(eventHelper) {
eventHelper.finish();
SimpleTest.finish();
});
}
// ensure Promise API is enabled
SpecialPowers.pushPrefEnv({
"set": [
["dom.promise.enabled", true],
['media.navigator.permission.disabled', true]
]
}, test);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,110 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Recording Status with multiple gUM requests</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.7" src="RecordingStatusHelper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">
'use strict';
SimpleTest.waitForExplicitFinish();
function test() {
let localStreams = [];
TestInit().then(function(eventHelper) {
/* step 1: create one audio stream
* expect: see one mozChromeEvent for recording start.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: false
}, eventHelper);
let gumDeferred = expectStream({ audio: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 2: create another audio stream
* expect: no mozChromeEvent after audio stream is created
*/
let gumDeferred = expectStream({ audio: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([Promise.resolve(eventHelper), gumDeferred]);
}).then(function([eventHelper]) {
/* step 3: create video stream
* expect: see one mozChromeEvent for recording start
*/
let eventDeferred = expectEvent({ active: true,
isAudio: true,
isVideo: true
}, eventHelper);
let gumDeferred = expectStream({ video: true,
fake: true
}, function(stream) {
localStreams.push(stream);
});
return expectAll([eventDeferred, gumDeferred]);
}).then(function([eventHelper]) {
/* step 4: stop first audio stream
* expect: no mozChromeEvent after first audio stream is stopped
*/
localStreams.shift().stop();
info('stop the first audio stream');
return Promise.resolve(eventHelper);
}).then(function(eventHelper) {
/* step 5: stop the second audio stream
* expect: see one mozChromeEvent for audio recording stop.
*/
let eventDeferred = expectEvent({ active: true,
isAudio: false,
isVideo: true
}, eventHelper);
localStreams.shift().stop();
info('stop the second audio stream');
return eventDeferred;
}).then(function(eventHelper) {
/* step 6: stop the video stream
* expect: see one mozChromeEvent for video recording stop.
*/
let eventDeferred = expectEvent({ active: false,
isAudio: false,
isVideo: false
}, eventHelper);
localStreams.shift().stop();
info('stop the video stream');
return eventDeferred;
}).then(function(eventHelper) {
eventHelper.finish();
SimpleTest.finish();
});
}
// ensure Promise API is enabled
SpecialPowers.pushPrefEnv({
"set": [
["dom.promise.enabled", true],
['media.navigator.permission.disabled', true]
]
}, test);
</script>
</pre>
</body>
</html>

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

@ -8,4 +8,6 @@ DEFINES['AB_CD'] = CONFIG['MOZ_UI_LOCALE']
DEFINES['PACKAGE'] = 'browser'
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
JAR_MANIFESTS += ['jar.mn']
JAR_MANIFESTS += ['jar.mn']
TEST_DIRS += ['content/test/mochitest']

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>

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

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>

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

@ -1,4 +1,4 @@
{
"revision": "5116c92a2905f6646d7049ddd1e1ab68eeb278d9",
"revision": "407993cc2cef77f8c8d0415f11996889ed18dc56",
"repo_path": "/integration/gaia-central"
}

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -10,7 +10,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f382061fe95750d584a9078175c421a36892afc9"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -7035,7 +7035,7 @@ if test "$OS_TARGET" = Android; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=memccpy,--wrap=memchr,--wrap=memrchr,--wrap=memcmp,--wrap=memcpy,--wrap=memmove,--wrap=memset,--wrap=memmem,--wrap=memswap,--wrap=index,--wrap=strchr,--wrap=strrchr,--wrap=strlen,--wrap=strcmp,--wrap=strcpy,--wrap=strcat,--wrap=strcasecmp,--wrap=strncasecmp,--wrap=strstr,--wrap=strcasestr,--wrap=strtok,--wrap=strtok_r,--wrap=strerror,--wrap=strerror_r,--wrap=strnlen,--wrap=strncat,--wrap=strncmp,--wrap=strncpy,--wrap=strlcat,--wrap=strlcpy,--wrap=strcspn,--wrap=strpbrk,--wrap=strsep,--wrap=strspn,--wrap=strcoll,--wrap=strxfrm"
fi
if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill"
fi
fi

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

@ -2693,7 +2693,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
AppDownloadManager.remove(aNewApp.manifestURL);
return [id, newManifest];
return [oldApp.id, newManifest];
}).bind(this)).then(
aOnSuccess,
@ -3150,7 +3150,8 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
aOldApp.appStatus = AppsUtils.getAppManifestStatus(newManifest);
this._saveEtag(aIsUpdate, aOldApp, aRequestChannel, aHash, newManifest);
this._checkOrigin(aIsSigned, aOldApp, newManifest, aIsUpdate);
this._checkOrigin(aIsSigned || aIsLocalFileInstall, aOldApp, newManifest,
aIsUpdate);
this._getIds(aIsSigned, aZipReader, converter, aNewApp, aOldApp, aIsUpdate);
return newManifest;
@ -3231,7 +3232,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
if (aIsUpdate) {
// Changing the origin during an update is not allowed.
if (uri.prePath != app.origin) {
if (uri.prePath != aOldApp.origin) {
throw "INVALID_ORIGIN_CHANGE";
}
// Nothing else to do for an update... since the
@ -3239,24 +3240,25 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
// app nor can we have a duplicated origin
} else {
debug("Setting origin to " + uri.prePath +
" for " + app.manifestURL);
" for " + aOldApp.manifestURL);
let newId = uri.prePath.substring(6); // "app://".length
if (newId in this.webapps) {
throw "DUPLICATE_ORIGIN";
}
aOldApp.origin = uri.prePath;
// Update the registry.
let oldId = aOldApp.id;
aOldApp.id = newId;
this.webapps[newId] = aOldApp;
delete this.webapps[aId];
delete this.webapps[oldId];
// Rename the directories where the files are installed.
[DIRECTORY_NAME, "TmpD"].forEach(function(aDir) {
let parent = FileUtils.getDir(aDir, ["webapps"], true, true);
let dir = FileUtils.getDir(aDir, ["webapps", aId], true, true);
let dir = FileUtils.getDir(aDir, ["webapps", oldId], true, true);
dir.moveTo(parent, newId);
});
// Signals that we need to swap the old id with the new app.
this.broadcastMessage("Webapps:RemoveApp", { id: aId });
this.broadcastMessage("Webapps:RemoveApp", { id: oldId });
this.broadcastMessage("Webapps:AddApp", { id: newId,
app: aOldApp });
}

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

@ -586,18 +586,33 @@ BrowserElementParent.prototype = {
_sendTouchEvent: function(type, identifiers, touchesX, touchesY,
radiisX, radiisY, rotationAngles, forces,
count, modifiers) {
this._sendAsyncMsg("send-touch-event", {
"type": type,
"identifiers": identifiers,
"touchesX": touchesX,
"touchesY": touchesY,
"radiisX": radiisX,
"radiisY": radiisY,
"rotationAngles": rotationAngles,
"forces": forces,
"count": count,
"modifiers": modifiers
});
let tabParent = this._frameLoader.tabParent;
if (tabParent && tabParent.useAsyncPanZoom) {
tabParent.injectTouchEvent(type,
identifiers,
touchesX,
touchesY,
radiisX,
radiisY,
rotationAngles,
forces,
count,
modifiers);
} else {
this._sendAsyncMsg("send-touch-event", {
"type": type,
"identifiers": identifiers,
"touchesX": touchesX,
"touchesY": touchesY,
"radiisX": radiisX,
"radiisY": radiisY,
"rotationAngles": rotationAngles,
"forces": forces,
"count": count,
"modifiers": modifiers
});
}
},
_goBack: function() {

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

@ -5,8 +5,19 @@
#include "domstubs.idl"
// Sole purpose is to be able to identify the concrete class nsTabParent
[uuid(95c7c50b-6677-456f-9f1e-885e1cc272dc)]
[scriptable, uuid(c402d6c2-837d-11e3-b47c-3c970e9f4238)]
interface nsITabParent : nsISupports
{
void injectTouchEvent(in AString aType,
[array, size_is(count)] in uint32_t aIdentifiers,
[array, size_is(count)] in int32_t aXs,
[array, size_is(count)] in int32_t aYs,
[array, size_is(count)] in uint32_t aRxs,
[array, size_is(count)] in uint32_t aRys,
[array, size_is(count)] in float aRotationAngles,
[array, size_is(count)] in float aForces,
in uint32_t count,
in long aModifiers);
readonly attribute boolean useAsyncPanZoom;
};

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

@ -1897,5 +1897,49 @@ TabParent::GetLoadContext()
return loadContext.forget();
}
NS_IMETHODIMP
TabParent::InjectTouchEvent(const nsAString& aType,
uint32_t* aIdentifiers,
int32_t* aXs,
int32_t* aYs,
uint32_t* aRxs,
uint32_t* aRys,
float* aRotationAngles,
float* aForces,
uint32_t aCount,
int32_t aModifiers)
{
uint32_t msg;
nsContentUtils::GetEventIdAndAtom(aType, NS_TOUCH_EVENT, &msg);
if (msg != NS_TOUCH_START && msg != NS_TOUCH_MOVE &&
msg != NS_TOUCH_END && msg != NS_TOUCH_CANCEL) {
return NS_ERROR_FAILURE;
}
WidgetTouchEvent event(true, msg, nullptr);
event.modifiers = aModifiers;
event.time = PR_IntervalNow();
event.touches.SetCapacity(aCount);
for (uint32_t i = 0; i < aCount; ++i) {
nsRefPtr<Touch> t = new Touch(aIdentifiers[i],
nsIntPoint(aXs[i], aYs[i]),
nsIntPoint(aRxs[i], aRys[i]),
aRotationAngles[i],
aForces[i]);
event.touches.AppendElement(t);
}
SendRealTouchEvent(event);
return NS_OK;
}
NS_IMETHODIMP
TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
{
*useAsyncPanZoom = UseAsyncPanZoom();
return NS_OK;
}
} // namespace tabs
} // namespace mozilla

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

@ -59,6 +59,9 @@ class TabParent : public PBrowserParent
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
public:
// nsITabParent
NS_DECL_NSITABPARENT
TabParent(ContentParent* aManager, const TabContext& aContext, uint32_t aChromeFlags);
virtual ~TabParent();
Element* GetOwnerElement() const { return mFrameElement; }

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

@ -91,6 +91,7 @@ let NotificationDB = {
var promise = OS.File.open(NOTIFICATION_STORE_PATH, {create: true});
promise.then(
function onSuccess(handle) {
handle.close();
callback && callback();
},
function onFailure(reason) {

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

@ -174,6 +174,9 @@ public:
RefPtr<UnixSocketConsumer> mConsumer;
private:
void FireSocketError();
/**
* libevent triggered functions that reads data from socket when available and
* guarenteed non-blocking. Only to be called on IO thread.
@ -487,29 +490,47 @@ void ShutdownSocketTask::Run()
}
void
UnixSocketImpl::Accept()
UnixSocketImpl::FireSocketError()
{
MOZ_ASSERT(!NS_IsMainThread());
if (!mConnector) {
NS_WARNING("No connector object available!");
return;
}
// Clean up watchers, statuses, fds
mReadWatcher.StopWatchingFileDescriptor();
mWriteWatcher.StopWatchingFileDescriptor();
mConnectionStatus = SOCKET_DISCONNECTED;
mFd.reset(-1);
// Tell the main thread we've errored
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
}
void
UnixSocketImpl::Accept()
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mConnector);
// This will set things we don't particularly care about, but it will hand
// back the correct structure size which is what we do care about.
if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) {
NS_WARNING("Cannot create socket address!");
FireSocketError();
return;
}
if (mFd.get() < 0) {
mFd = mConnector->Create();
if (mFd.get() < 0) {
NS_WARNING("Cannot create socket fd!");
FireSocketError();
return;
}
if (!SetSocketFlags()) {
NS_WARNING("Cannot set socket flags!");
FireSocketError();
return;
}
@ -517,6 +538,7 @@ UnixSocketImpl::Accept()
#ifdef DEBUG
CHROMIUM_LOG("...bind(%d) gave errno %d", mFd.get(), errno);
#endif
FireSocketError();
return;
}
@ -524,15 +546,13 @@ UnixSocketImpl::Accept()
#ifdef DEBUG
CHROMIUM_LOG("...listen(%d) gave errno %d", mFd.get(), errno);
#endif
FireSocketError();
return;
}
if (!mConnector->SetUpListenSocket(mFd)) {
NS_WARNING("Could not set up listen socket!");
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}
@ -545,15 +565,13 @@ void
UnixSocketImpl::Connect()
{
MOZ_ASSERT(!NS_IsMainThread());
if (!mConnector) {
NS_WARNING("No connector object available!");
return;
}
MOZ_ASSERT(mConnector);
if (mFd.get() < 0) {
mFd = mConnector->Create();
if (mFd.get() < 0) {
NS_WARNING("Cannot create socket fd!");
FireSocketError();
return;
}
}
@ -562,15 +580,14 @@ UnixSocketImpl::Connect()
if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
NS_WARNING("Cannot create socket address!");
FireSocketError();
return;
}
// Select non-blocking IO.
if (-1 == fcntl(mFd.get(), F_SETFL, O_NONBLOCK)) {
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
NS_WARNING("Cannot set nonblock!");
FireSocketError();
return;
}
@ -583,20 +600,12 @@ UnixSocketImpl::Connect()
int current_opts = fcntl(mFd.get(), F_GETFL, 0);
if (-1 == current_opts) {
NS_WARNING("Cannot get socket opts!");
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}
if (-1 == fcntl(mFd.get(), F_SETFL, current_opts & ~O_NONBLOCK)) {
NS_WARNING("Cannot set socket opts to blocking!");
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}
@ -616,20 +625,19 @@ UnixSocketImpl::Connect()
#if DEBUG
CHROMIUM_LOG("Socket connect errno=%d\n", errno);
#endif
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}
if (!SetSocketFlags()) {
NS_WARNING("Cannot set socket flags!");
FireSocketError();
return;
}
if (!mConnector->SetUp(mFd)) {
NS_WARNING("Could not set up socket!");
FireSocketError();
return;
}
@ -862,30 +870,19 @@ UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
if (ret || error) {
NS_WARNING("getsockopt failure on async socket connect!");
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}
if (!SetSocketFlags()) {
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
NS_WARNING("Cannot set socket flags!");
FireSocketError();
return;
}
if (!mConnector->SetUp(mFd)) {
NS_WARNING("Could not set up socket!");
mFd.reset(-1);
nsRefPtr<OnSocketEventTask> t =
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
NS_DispatchToMainThread(t);
mConnectionStatus = SOCKET_DISCONNECTED;
FireSocketError();
return;
}

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

@ -15,9 +15,11 @@
#include <pthread.h>
#include <alloca.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <vector>
#include "mozilla/LinkedList.h"
@ -25,6 +27,10 @@
using namespace mozilla;
extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(__NR_tgkill, tgid, tid, signalno);
}
/**
* Provides the wrappers to a selected set of pthread and system-level functions
* as the basis for implementing Zygote-like preforking mechanism.
@ -62,7 +68,6 @@ int __real_pipe2(int __pipedes[2], int flags);
int __real_pipe(int __pipedes[2]);
int __real_epoll_ctl(int aEpollFd, int aOp, int aFd, struct epoll_event *aEvent);
int __real_close(int aFd);
}
#define REAL(s) __real_##s
@ -139,6 +144,8 @@ TLSInfoList;
#define NUWA_STACK_SIZE (1024 * 32)
#endif
#define NATIVE_THREAD_NAME_LENGTH 16
struct thread_info : public mozilla::LinkedListElement<thread_info> {
pthread_t origThreadID;
pthread_t recreatedThreadID;
@ -160,6 +167,10 @@ struct thread_info : public mozilla::LinkedListElement<thread_info> {
pthread_mutex_t *reacquireMutex;
void *stk;
pid_t origNativeThreadID;
pid_t recreatedNativeThreadID;
char nativeThreadName[NATIVE_THREAD_NAME_LENGTH];
};
typedef struct thread_info thread_info_t;
@ -212,6 +223,7 @@ static TLSKeySet sTLSKeys;
*/
static pthread_mutex_t sThreadFreezeLock = PTHREAD_MUTEX_INITIALIZER;
static thread_info_t sMainThread;
static LinkedList<thread_info_t> sAllThreads;
static int sThreadCount = 0;
static int sThreadFreezeCount = 0;
@ -277,6 +289,32 @@ GetThreadInfo(pthread_t threadID) {
return tinfo;
}
/**
* Get thread info using the specified native thread ID.
*
* @return thread_info_t with nativeThreadID == specified threadID
*/
static thread_info_t*
GetThreadInfo(pid_t threadID) {
if (sIsNuwaProcess) {
REAL(pthread_mutex_lock)(&sThreadCountLock);
}
thread_info_t *thrinfo = nullptr;
for (thread_info_t *tinfo = sAllThreads.getFirst();
tinfo;
tinfo = tinfo->getNext()) {
if (tinfo->origNativeThreadID == threadID) {
thrinfo = tinfo;
break;
}
}
if (sIsNuwaProcess) {
pthread_mutex_unlock(&sThreadCountLock);
}
return thrinfo;
}
#if !defined(HAVE_THREAD_TLS_KEYWORD)
/**
* Get thread info of the current thread.
@ -449,6 +487,7 @@ thread_info_new(void) {
tinfo->recrFunc = nullptr;
tinfo->recrArg = nullptr;
tinfo->recreatedThreadID = 0;
tinfo->recreatedNativeThreadID = 0;
tinfo->reacquireMutex = nullptr;
tinfo->stk = malloc(NUWA_STACK_SIZE);
pthread_attr_init(&tinfo->threadAttr);
@ -497,6 +536,7 @@ _thread_create_startup(void *arg) {
SET_THREAD_INFO(tinfo);
tinfo->origThreadID = REAL(pthread_self)();
tinfo->origNativeThreadID = gettid();
pthread_cleanup_push(thread_info_cleanup, tinfo);
@ -619,6 +659,7 @@ RestoreTLSInfo(thread_info_t *tinfo) {
SET_THREAD_INFO(tinfo);
tinfo->recreatedThreadID = REAL(pthread_self)();
tinfo->recreatedNativeThreadID = gettid();
}
extern "C" MFBT_API int
@ -1215,6 +1256,27 @@ __wrap_close(int aFd) {
return rv;
}
extern "C" MFBT_API int
__wrap_tgkill(pid_t tgid, pid_t tid, int signalno)
{
if (sIsNuwaProcess) {
return tgkill(tgid, tid, signalno);
}
if (tid == sMainThread.origNativeThreadID) {
return tgkill(tgid, sMainThread.recreatedNativeThreadID, signalno);
}
thread_info_t *tinfo = (tid == sMainThread.origNativeThreadID ?
&sMainThread :
GetThreadInfo(tid));
if (!tinfo) {
return tgkill(tgid, tid, signalno);
}
return tgkill(tgid, tinfo->recreatedNativeThreadID, signalno);
}
static void *
thread_recreate_startup(void *arg) {
/*
@ -1232,6 +1294,7 @@ thread_recreate_startup(void *arg) {
*/
thread_info_t *tinfo = (thread_info_t *)arg;
prctl(PR_SET_NAME, (unsigned long)&tinfo->nativeThreadName, 0, 0, 0);
RestoreTLSInfo(tinfo);
if (setjmp(tinfo->retEnv) != 0) {
@ -1267,6 +1330,9 @@ RecreateThreads() {
sIsNuwaProcess = false;
sIsFreezing = false;
sMainThread.recreatedThreadID = pthread_self();
sMainThread.recreatedNativeThreadID = gettid();
// Run registered constructors.
for (std::vector<nuwa_construct_t>::iterator ctr = sConstructors.begin();
ctr != sConstructors.end();
@ -1556,6 +1622,10 @@ PrepareNuwaProcess() {
// Make marked threads block in one freeze point.
REAL(pthread_mutex_lock)(&sThreadFreezeLock);
// Populate sMainThread for mapping of tgkill.
sMainThread.origThreadID = pthread_self();
sMainThread.origNativeThreadID = gettid();
}
// Make current process as a Nuwa process.
@ -1607,6 +1677,10 @@ NuwaMarkCurrentThread(void (*recreate)(void *), void *arg) {
tinfo->flags |= TINFO_FLAG_NUWA_SUPPORT;
tinfo->recrFunc = recreate;
tinfo->recrArg = arg;
// XXX Thread name might be set later than this call. If this is the case, we
// might need to delay getting the thread name.
prctl(PR_GET_NAME, (unsigned long)&tinfo->nativeThreadName, 0, 0, 0);
}
/**

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

@ -9,6 +9,7 @@
"toolkit/devtools/apps": ""
},
"excludetests": {
"b2g/chrome/content/test/mochitest": "require OOP support for mochitest-b2g-desktop, Bug 957554",
"content/xul":"tests that use xul",
"layout/xul" : "",
"dom/tests/mochitest/general/test_focusrings.xul":"",

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

@ -83,6 +83,10 @@
#include <stdio.h>
#include <list>
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
#define SIGNAL_SAVE_PROFILE SIGUSR2
#if defined(__GLIBC__)
@ -229,9 +233,15 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
sem_post(&sSignalHandlingDone);
}
// If the Nuwa process is enabled, we need to use the wrapper of tgkill() to
// perform the mapping of thread ID.
#ifdef MOZ_NUWA_PROCESS
extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno);
#else
int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(SYS_tgkill, tgid, tid, signalno);
}
#endif
class PlatformData : public Malloced {
public:
@ -263,6 +273,18 @@ static void* SignalSender(void* arg) {
static void* initialize_atfork = setup_atfork();
# endif
#ifdef MOZ_NUWA_PROCESS
// If the Nuwa process is enabled, we need to mark and freeze the sampler
// thread in the Nuwa process and have this thread recreated in the spawned
// child.
if(IsNuwaProcess()) {
NuwaMarkCurrentThread(nullptr, nullptr);
// Freeze the thread here so the spawned child will get the correct tgid
// from the getpid() call below.
NuwaFreezeCurrentThread();
}
#endif
int vm_tgid_ = getpid();
while (SamplerRegistry::sampler->IsActive()) {