diff --git a/b2g/chrome/content/payment.js b/b2g/chrome/content/payment.js index 2eb3cbb5c973..7fb4c2999a5b 100644 --- a/b2g/chrome/content/payment.js +++ b/b2g/chrome/content/payment.js @@ -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: , + // mnc: , + // iccId: , + // dataPrimary: + // }, + // "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); }); diff --git a/b2g/chrome/content/test/mochitest/RecordingStatusChromeScript.js b/b2g/chrome/content/test/mochitest/RecordingStatusChromeScript.js new file mode 100644 index 000000000000..48f8bd550b19 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/RecordingStatusChromeScript.js @@ -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'); +}); diff --git a/b2g/chrome/content/test/mochitest/RecordingStatusHelper.js b/b2g/chrome/content/test/mochitest/RecordingStatusHelper.js new file mode 100644 index 000000000000..5e3e6814e1fd --- /dev/null +++ b/b2g/chrome/content/test/mochitest/RecordingStatusHelper.js @@ -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'); + }); +} diff --git a/b2g/chrome/content/test/mochitest/file_getusermedia_iframe.html b/b2g/chrome/content/test/mochitest/file_getusermedia_iframe.html new file mode 100644 index 000000000000..f2b18eab3f42 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/file_getusermedia_iframe.html @@ -0,0 +1,36 @@ + + + + Iframe for Recording Status + + + + + + +
+
+
+ + diff --git a/b2g/chrome/content/test/mochitest/mochitest.ini b/b2g/chrome/content/test/mochitest/mochitest.ini new file mode 100644 index 000000000000..a9160bca7c53 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/mochitest.ini @@ -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] diff --git a/b2g/chrome/content/test/mochitest/moz.build b/b2g/chrome/content/test/mochitest/moz.build new file mode 100644 index 000000000000..8421b15157a7 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/moz.build @@ -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'] diff --git a/b2g/chrome/content/test/mochitest/test_recordingStatus_basic.html b/b2g/chrome/content/test/mochitest/test_recordingStatus_basic.html new file mode 100644 index 000000000000..9bc6b18038f2 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/test_recordingStatus_basic.html @@ -0,0 +1,121 @@ + + + + Test for Recording Status + + + + + + +
+
+
+ + diff --git a/b2g/chrome/content/test/mochitest/test_recordingStatus_iframe.html b/b2g/chrome/content/test/mochitest/test_recordingStatus_iframe.html new file mode 100644 index 000000000000..c7987eeae64e --- /dev/null +++ b/b2g/chrome/content/test/mochitest/test_recordingStatus_iframe.html @@ -0,0 +1,73 @@ + + + + Test for Recording Status in iframe + + + + + + +
+
+
+
+ + diff --git a/b2g/chrome/content/test/mochitest/test_recordingStatus_kill_content_process.html b/b2g/chrome/content/test/mochitest/test_recordingStatus_kill_content_process.html new file mode 100644 index 000000000000..bef89159b719 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/test_recordingStatus_kill_content_process.html @@ -0,0 +1,74 @@ + + + + Test for Recording Status after process shutdown + + + + + + +
+
+
+ + diff --git a/b2g/chrome/content/test/mochitest/test_recordingStatus_multiple_requests.html b/b2g/chrome/content/test/mochitest/test_recordingStatus_multiple_requests.html new file mode 100644 index 000000000000..b4367fa649e9 --- /dev/null +++ b/b2g/chrome/content/test/mochitest/test_recordingStatus_multiple_requests.html @@ -0,0 +1,110 @@ + + + + Test for Recording Status with multiple gUM requests + + + + + + +
+
+
+ + diff --git a/b2g/chrome/moz.build b/b2g/chrome/moz.build index bea69e096ae5..9d39af7ef428 100644 --- a/b2g/chrome/moz.build +++ b/b2g/chrome/moz.build @@ -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'] \ No newline at end of file +JAR_MANIFESTS += ['jar.mn'] + +TEST_DIRS += ['content/test/mochitest'] diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 3c8460491975..f420934d1836 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index eb439f4c842c..c13d9713c3af 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 3c8460491975..f420934d1836 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 9c1b61bd2d01..c3b263b4b66e 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "5116c92a2905f6646d7049ddd1e1ab68eeb278d9", + "revision": "407993cc2cef77f8c8d0415f11996889ed18dc56", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 52af1823c6d0..c04faa124790 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 0d8b620b6fd2..3074f7eac0a4 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 7cdd1237743c..5a8bd5d739ad 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index c05baba44842..4ba5fd588d6d 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index c509b832677f..de8067c2006f 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 24201ac4a244..0aaa353ec42f 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/configure.in b/configure.in index 52f752932f6f..de9c1c2a6535 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 1546088519c4..57a4acc44222 100755 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -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 }); } diff --git a/dom/browser-element/BrowserElementParent.jsm b/dom/browser-element/BrowserElementParent.jsm index e9a5a6ccffce..7c36d2f4d308 100644 --- a/dom/browser-element/BrowserElementParent.jsm +++ b/dom/browser-element/BrowserElementParent.jsm @@ -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() { diff --git a/dom/interfaces/base/nsITabParent.idl b/dom/interfaces/base/nsITabParent.idl index c69d57506b82..1f5705868d09 100644 --- a/dom/interfaces/base/nsITabParent.idl +++ b/dom/interfaces/base/nsITabParent.idl @@ -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; }; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 9212e6ca0ebe..740d1cfcb0b9 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -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 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 diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 80a823bd64f5..7c4fbee09253 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -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; } diff --git a/dom/src/notification/NotificationDB.jsm b/dom/src/notification/NotificationDB.jsm index dd590c175266..34f3028bb0e0 100644 --- a/dom/src/notification/NotificationDB.jsm +++ b/dom/src/notification/NotificationDB.jsm @@ -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) { diff --git a/ipc/unixsocket/UnixSocket.cpp b/ipc/unixsocket/UnixSocket.cpp index 2593b3f86bcb..9ecd7f79fe69 100644 --- a/ipc/unixsocket/UnixSocket.cpp +++ b/ipc/unixsocket/UnixSocket.cpp @@ -174,6 +174,9 @@ public: RefPtr 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 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 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 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 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 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 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 t = - new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR); - NS_DispatchToMainThread(t); - mConnectionStatus = SOCKET_DISCONNECTED; + FireSocketError(); return; } if (!SetSocketFlags()) { - mFd.reset(-1); - nsRefPtr 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 t = - new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR); - NS_DispatchToMainThread(t); - mConnectionStatus = SOCKET_DISCONNECTED; + FireSocketError(); return; } diff --git a/mozglue/build/Nuwa.cpp b/mozglue/build/Nuwa.cpp index 44f56bad8790..4a1942a211e2 100644 --- a/mozglue/build/Nuwa.cpp +++ b/mozglue/build/Nuwa.cpp @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #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 { pthread_t origThreadID; pthread_t recreatedThreadID; @@ -160,6 +167,10 @@ struct thread_info : public mozilla::LinkedListElement { 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 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::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); } /** diff --git a/testing/mochitest/b2g-desktop.json b/testing/mochitest/b2g-desktop.json index a77ed2766075..f13dfaa5ee26 100644 --- a/testing/mochitest/b2g-desktop.json +++ b/testing/mochitest/b2g-desktop.json @@ -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":"", diff --git a/tools/profiler/platform-linux.cc b/tools/profiler/platform-linux.cc index cb129c1ea8a7..7afb27df3d4c 100644 --- a/tools/profiler/platform-linux.cc +++ b/tools/profiler/platform-linux.cc @@ -83,6 +83,10 @@ #include #include +#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()) {