Merge m-c to inbound, a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-05-26 16:25:11 -07:00
commit fd401f9e9b
122 changed files with 2210 additions and 1074 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,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="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="53517af1f57810bef745f03602f407161a475d76"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -129,7 +129,7 @@
<!-- Emulator specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="f390788a00706c06e5248edfd8d27b365387e84a"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="fbd2becab3825c49e756db5149552f85049c66e2"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="82800d4ed59429da2ba628d4558ea0653a902a4e"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="0c6a6547cd1fd302fa2b0f6e375654df36bf0ec4"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="42f61f665e7a9857da8cd14b455e15bae98e6b44"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,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="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "0d67eb545b2f4342525d14e1a10e80c91c87ada0",
"git_revision": "8ca93673869a64e09ed6153c5402896822dfb253",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "da5a1b63adc3322a73c57d22690472857fed353a",
"revision": "ce5b9f84c7e253e3615694be152d257995ce2238",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="53517af1f57810bef745f03602f407161a475d76"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="0d67eb545b2f4342525d14e1a10e80c91c87ada0"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -330,6 +330,7 @@ loop.conversationViews = (function(mozL10n) {
var errorString;
switch (this.props.failureReason) {
case FAILURE_DETAILS.NO_MEDIA:
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
errorString = mozL10n.get("no_media_failure_message");
break;

View File

@ -330,6 +330,7 @@ loop.conversationViews = (function(mozL10n) {
var errorString;
switch (this.props.failureReason) {
case FAILURE_DETAILS.NO_MEDIA:
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
errorString = mozL10n.get("no_media_failure_message");
break;

View File

@ -978,7 +978,7 @@ loop.OTSdkDriver = (function() {
var bucket = this.mozLoop.SHARING_STATE_CHANGE[type.toUpperCase() + "_" +
(enabled ? "ENABLED" : "DISABLED")];
if (!bucket) {
if (typeof bucket === "undefined") {
console.error("No sharing state bucket found for '" + type + "'");
return;
}

View File

@ -241,7 +241,7 @@ let LoopRoomsInternal = {
* @param {String} roomToken The token for the room that needs encrypting.
*/
queueForEncryption: function(roomToken) {
if (!this.encryptionQueue.queue.includes(roomToken)) {
if (this.encryptionQueue.queue.indexOf(roomToken) == -1) {
this.encryptionQueue.queue.push(roomToken);
}

View File

@ -939,6 +939,15 @@ describe("loop.conversationViews", function () {
expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message");
});
it("should show 'no media' for FAILURE_DETAILS.NO_MEDIA reason", function() {
view = mountTestComponent({
cancelCall: function() {},
failureReason: FAILURE_DETAILS.NO_MEDIA
});
expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message");
});
it("should show 'generic_failure_title' when no reason is specified", function() {
view = mountTestComponent({cancelCall: function() {}});

View File

@ -78,16 +78,16 @@ describe("loop.OTSdkDriver", function () {
mozLoop = {
telemetryAddValue: sinon.stub(),
TWO_WAY_MEDIA_CONN_LENGTH: {
SHORTER_THAN_10S: "SHORTER_THAN_10S",
BETWEEN_10S_AND_30S: "BETWEEN_10S_AND_30S",
BETWEEN_30S_AND_5M: "BETWEEN_30S_AND_5M",
MORE_THAN_5M: "MORE_THAN_5M"
SHORTER_THAN_10S: 0,
BETWEEN_10S_AND_30S: 1,
BETWEEN_30S_AND_5M: 2,
MORE_THAN_5M: 3
},
SHARING_STATE_CHANGE: {
WINDOW_ENABLED: "WINDOW_ENABLED",
WINDOW_DISABLED: "WINDOW_DISABLED",
BROWSER_ENABLED: "BROWSER_ENABLED",
BROWSER_DISABLED: "BROWSER_DISABLED"
WINDOW_ENABLED: 0,
WINDOW_DISABLED: 1,
BROWSER_ENABLED: 2,
BROWSER_DISABLED: 3
}
};

View File

@ -190,8 +190,8 @@ EventEmitter.decorate(this);
/**
* DOM query helpers.
*/
function $(selector, target = document) target.querySelector(selector);
function $all(selector, target = document) target.querySelectorAll(selector);
let $ = (selector, target = document) => target.querySelector(selector);
let $all = (selector, target = document) => target.querySelectorAll(selector);
/**
* Helper for getting an nsIURL instance out of a string.

View File

@ -57,7 +57,9 @@ CanvasDebuggerPanel.prototype = {
// DevToolPanel API
get target() this._toolbox.target,
get target() {
return this._toolbox.target;
},
destroy: function() {
// Make sure this panel is not already destroyed.

View File

@ -6,7 +6,7 @@
* You can also use this initialization format as a template for other tests.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
ok(target, "Should have a target available.");

View File

@ -6,7 +6,7 @@
* and that their stack is successfully retrieved.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* for a canvas context.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* the correct thumbnails.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* the correct "end result" screenshot.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -5,7 +5,7 @@
* Tests if screenshots for arbitrary draw calls are generated properly.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* by deferring the the most recent previous draw-call.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* forms if the method's signature does not expect an enum. Bug 999687.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* forms if the method's signature does not expect an enum. Bug 999687.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_ENUM_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* after generating screenshots using the actor.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_BINDINGS_URL);
loadFrameScripts();

View File

@ -6,7 +6,7 @@
* for a canvas context, and that the generated screenshots are correct.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SET_TIMEOUT_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* in the event no rAF loop is found.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(NO_CANVAS_URL);
loadFrameScripts();

View File

@ -5,7 +5,7 @@
* Tests if certain function calls are properly highlighted in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if filtering the items in the call list works properly.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
let searchbox = $("#calls-searchbox");

View File

@ -5,7 +5,7 @@
* Tests if the a function call's stack is properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* and jumping to source in the debugger for the topmost call item works.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* on a function call item.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if clearing the snapshots list works as expected.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if screenshots are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if thumbnails are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* function call items and their respective screenshots.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly configured when opening the tool.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { $ } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests whether the frontend behaves correctly while reording a snapshot.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests whether the frontend displays a placeholder snapshot while recording.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, L10N, $, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* after finishing recording.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -8,7 +8,7 @@
* of its loop, when the recording starts before the rAFs start.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(RAF_BEGIN_URL);
let { window, EVENTS, gFront, SnapshotsListView } = panel.panelWin;
loadFrameScripts();

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly reconfigured after reloading.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly reconfigured after reloading.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the slider in the calls list view works as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the slider in the calls list view works as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* respective to their recorded animation frame.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* respective to their recorded animation frame.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the stepping buttons in the call list toolbar work as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that you can stop a recording that does not have a rAF cycle.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that a recording that does not have a rAF cycle fails after timeout.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* after timeout.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(RAF_NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -66,6 +66,7 @@ support-files =
[browser_graphs-09f.js]
[browser_graphs-10a.js]
[browser_graphs-10b.js]
[browser_graphs-10c.js]
[browser_graphs-11a.js]
[browser_graphs-11b.js]
[browser_graphs-12.js]

View File

@ -0,0 +1,86 @@
// Tests that graphs properly handle resizing.
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
add_task(function*() {
yield promiseTab("about:blank");
yield performTest();
gBrowser.removeCurrentTab();
});
function* performTest() {
let [host, win, doc] = yield createHost("window");
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
let graph = new LineGraphWidget(doc.body, "fps");
yield graph.once("ready");
let refreshCount = 0;
graph.on("refresh", () => refreshCount++);
yield testGraph(host, graph);
is(refreshCount, 2, "The graph should've been refreshed 2 times.");
yield graph.destroy();
host.destroy();
}
function* testGraph(host, graph) {
graph.setData(TEST_DATA);
host._window.resizeTo(500, 500);
yield graph.once("refresh");
let oldBounds = host.frame.getBoundingClientRect();
is (graph._width, oldBounds.width * window.devicePixelRatio,
"The window was properly resized (1).");
is (graph._height, oldBounds.height * window.devicePixelRatio,
"The window was properly resized (1).");
dragStart(graph, 100);
dragStop(graph, 400);
is(graph.getSelection().start, 100,
"The current selection start value is correct (1).");
is(graph.getSelection().end, 400,
"The current selection end value is correct (1).");
info("Making sure the selection updates when the window is resized");
host._window.resizeTo(250, 250);
yield graph.once("refresh");
let newBounds = host.frame.getBoundingClientRect();
is (graph._width, newBounds.width * window.devicePixelRatio,
"The window was properly resized (2).");
is (graph._height, newBounds.height * window.devicePixelRatio,
"The window was properly resized (2).");
let ratio = oldBounds.width / newBounds.width;
info("The window resize ratio is: " + ratio);
is(graph.getSelection().start, Math.round(100 / ratio),
"The current selection start value is correct (2).");
is(graph.getSelection().end, Math.round(400 / ratio),
"The current selection end value is correct (2).");
}
// EventUtils just doesn't work!
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -684,6 +684,13 @@ AbstractCanvasGraph.prototype = {
return;
}
// Handle a changed size by mapping the old selection to the new width
if (this._width && newWidth && this.hasSelection()) {
let ratio = this._width / (newWidth * this._pixelRatio);
this._selection.start = Math.round(this._selection.start / ratio);
this._selection.end = Math.round(this._selection.end / ratio);
}
bounds.width = newWidth;
bounds.height = newHeight;
this._iframe.setAttribute("width", bounds.width);

View File

@ -151,6 +151,7 @@ function CssHtmlTree(aStyleInspector, aPageStyle)
this._onClick = this._onClick.bind(this);
this._onCopy = this._onCopy.bind(this);
this._onCopyColor = this._onCopyColor.bind(this);
this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
this._onFilterStyles = this._onFilterStyles.bind(this);
this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
this._onClearSearch = this._onClearSearch.bind(this);
@ -328,7 +329,7 @@ CssHtmlTree.prototype = {
* - type {String} One of the VIEW_NODE_XXX_TYPE const in
* style-inspector-overlays
* - value {Object} Depends on the type of the node
* returns null of the node isn't anything we care about
* returns null if the node isn't anything we care about
*/
getNodeInfo: function(node) {
if (!node) {
@ -351,7 +352,7 @@ CssHtmlTree.prototype = {
return {
type: overlays.VIEW_NODE_SELECTOR_TYPE,
value: selectorText.trim()
}
};
}
// Walk up the nodes to find out where node is
@ -715,6 +716,13 @@ CssHtmlTree.prototype = {
command: this._onCopyColor
});
// Copy data URI
this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
label: "styleinspector.contextmenu.copyImageDataUrl",
accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
command: this._onCopyImageDataUrl
});
// Show Original Sources
this.menuitemSources= createMenuItem(this._contextmenu, {
label: "ruleView.contextmenu.showOrigSources",
@ -745,6 +753,7 @@ CssHtmlTree.prototype = {
this.menuitemSources.setAttribute("checked", showOrig);
this.menuitemCopyColor.hidden = !this._isColorPopup();
this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
},
/**
@ -757,14 +766,12 @@ CssHtmlTree.prototype = {
_isColorPopup: function () {
this._colorToCopy = "";
let trigger = this.popupNode;
if (!trigger) {
let container = this._getPopupNodeContainer();
if (!container) {
return false;
}
let container = (trigger.nodeType == trigger.TEXT_NODE) ?
trigger.parentElement : trigger;
let isColorNode = el => el.dataset && "color" in el.dataset;
while (!isColorNode(container)) {
@ -778,6 +785,52 @@ CssHtmlTree.prototype = {
return true;
},
/**
* Check if the context menu popup was opened with a click on an image link
* If true, save the image url to this._imageUrlToCopy
*/
_isImageUrlPopup: function () {
this._imageUrlToCopy = "";
let container = this._getPopupNodeContainer();
let isImageUrlNode = this._isImageUrlNode(container);
if (isImageUrlNode) {
this._imageUrlToCopy = container.href;
}
return isImageUrlNode;
},
/**
* Check if a node is an image url
* @param {DOMNode} node The node which we want information about
* @return {Boolean} true if the node is an image url
*/
_isImageUrlNode: function (node) {
let nodeInfo = this.getNodeInfo(node);
if (!nodeInfo) {
return false
}
return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
},
/**
* Get the DOM Node container for the current popupNode.
* If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
* @return {DOMNode}
*/
_getPopupNodeContainer: function () {
let container = null;
let node = this.popupNode;
if (node) {
let isTextNode = node.nodeType == node.TEXT_NODE;
container = isTextNode ? node.parentElement : node;
}
return container;
},
/**
* Context menu handler.
*/
@ -821,6 +874,22 @@ CssHtmlTree.prototype = {
clipboardHelper.copyString(this._colorToCopy);
},
/**
* Retrieve the image data for the selected image url and copy it to the clipboard
*/
_onCopyImageDataUrl: Task.async(function*() {
let message;
try {
let inspectorFront = this.inspector.inspector;
let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
message = yield data.data.string();
} catch (e) {
message = CssHtmlTree.l10n("styleinspector.copyImageDataUrlError");
}
clipboardHelper.copyString(message);
}),
/**
* Copy selected text.
*
@ -910,6 +979,10 @@ CssHtmlTree.prototype = {
this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
this.menuitemCopyColor = null;
// Destroy Copy Data URI menuitem.
this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl);
this.menuitemCopyImageDataUrl = null;
// Destroy the context menu.
this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
this._contextmenu.parentNode.removeChild(this._contextmenu);

View File

@ -1123,6 +1123,7 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
this._onSelectAll = this._onSelectAll.bind(this);
this._onCopy = this._onCopy.bind(this);
this._onCopyColor = this._onCopyColor.bind(this);
this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
this._onFilterStyles = this._onFilterStyles.bind(this);
@ -1214,6 +1215,11 @@ CssRuleView.prototype = {
accesskey: "ruleView.contextmenu.copyColor.accessKey",
command: this._onCopyColor
});
this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
label: "styleinspector.contextmenu.copyImageDataUrl",
accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
command: this._onCopyImageDataUrl
});
this.menuitemSources = createMenuItem(this._contextmenu, {
label: "ruleView.contextmenu.showOrigSources",
accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
@ -1348,6 +1354,7 @@ CssRuleView.prototype = {
}
this.menuitemCopyColor.hidden = !this._isColorPopup();
this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
this.menuitemCopy.disabled = !copy;
var showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
@ -1398,7 +1405,7 @@ CssRuleView.prototype = {
pseudoElement: prop.rule.pseudoElement,
sheetHref: prop.rule.domRule.href
};
} else if (classes.contains("theme-link") && prop) {
} else if (classes.contains("theme-link") && !classes.contains("ruleview-rule-source") && prop) {
type = overlays.VIEW_NODE_IMAGE_URL_TYPE;
value = {
property: getPropertyNameAndValue(node).name,
@ -1430,14 +1437,11 @@ CssRuleView.prototype = {
_isColorPopup: function () {
this._colorToCopy = "";
let trigger = this.doc.popupNode;
if (!trigger) {
let container = this._getPopupNodeContainer();
if (!container) {
return false;
}
let container = (trigger.nodeType == trigger.TEXT_NODE) ?
trigger.parentElement : trigger;
let isColorNode = el => el.dataset && "color" in el.dataset;
while (!isColorNode(container)) {
@ -1451,6 +1455,52 @@ CssRuleView.prototype = {
return true;
},
/**
* Check if the context menu popup was opened with a click on an image link
* If true, save the image url to this._imageUrlToCopy
*/
_isImageUrlPopup: function () {
this._imageUrlToCopy = "";
let container = this._getPopupNodeContainer();
let isImageUrlNode = this._isImageUrlNode(container);
if (isImageUrlNode) {
this._imageUrlToCopy = container.href;
}
return isImageUrlNode;
},
/**
* Check if a node is an image url
* @param {DOMNode} node The node which we want information about
* @return {Boolean} true if the node is an image url
*/
_isImageUrlNode: function (node) {
let nodeInfo = this.getNodeInfo(node);
if (!nodeInfo) {
return false
}
return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
},
/**
* Get the DOM Node container for the current popupNode.
* If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
* @return {DOMNode}
*/
_getPopupNodeContainer: function () {
let container = null;
let node = this.doc.popupNode;
if (node) {
let isTextNode = node.nodeType == node.TEXT_NODE;
container = isTextNode ? node.parentElement : node;
}
return container;
},
/**
* Context menu handler.
*/
@ -1525,6 +1575,22 @@ CssRuleView.prototype = {
clipboardHelper.copyString(this._colorToCopy);
},
/**
* Retrieve the image data for the selected image url and copy it to the clipboard
*/
_onCopyImageDataUrl: Task.async(function*() {
let message;
try {
let inspectorFront = this.inspector.inspector;
let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
message = yield data.data.string();
} catch (e) {
message = _strings.GetStringFromName("styleinspector.copyImageDataUrlError");
}
clipboardHelper.copyString(message);
}),
/**
* Toggle the original sources pref.
*/
@ -1729,6 +1795,10 @@ CssRuleView.prototype = {
this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
this.menuitemCopyColor = null;
// Destroy Copy Data URI menuitem.
this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl);
this.menuitemCopyImageDataUrl = null;
this.menuitemSources.removeEventListener("command", this._onToggleOrigSources);
this.menuitemSources = null;

View File

@ -21,6 +21,7 @@ const {
SwatchFilterTooltip
} = require("devtools/shared/widgets/Tooltip");
const {CssLogic} = require("devtools/styleinspector/css-logic");
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Promise:promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -59,6 +60,8 @@ function HighlightersOverlay(view) {
// Only initialize the overlay if at least one of the highlighter types is
// supported
this.supportsHighlighters = this.highlighterUtils.supportsCustomHighlighters();
EventEmitter.decorate(this);
}
exports.HighlightersOverlay = HighlightersOverlay;
@ -124,9 +127,13 @@ HighlightersOverlay.prototype = {
if (type) {
this.highlighterShown = type;
let node = this.view.inspector.selection.nodeFront;
this._getHighlighter(type).then(highlighter => {
highlighter.show(node);
});
this._getHighlighter(type)
.then(highlighter => highlighter.show(node))
.then(shown => {
if (shown) {
this.emit("highlighter-shown");
}
});
}
},
@ -176,6 +183,7 @@ HighlightersOverlay.prototype = {
promise.then(null, Cu.reportError);
}
this.highlighterShown = null;
this.emit("highlighter-hidden");
});
}
},

View File

@ -151,6 +151,7 @@ skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
[browser_ruleview_user-property-reset.js]
[browser_styleinspector_context-menu-copy-color_01.js]
[browser_styleinspector_context-menu-copy-color_02.js]
[browser_styleinspector_context-menu-copy-data-uri.js]
[browser_styleinspector_csslogic-content-stylesheets.js]
[browser_styleinspector_output-parser.js]
[browser_styleinspector_refresh_when_active.js]

View File

@ -0,0 +1,99 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const PROPERTIES_URL = "chrome://global/locale/devtools/styleinspector.properties";
const TEST_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=";
// invalid URL still needs to be reachable otherwise getImageDataUrl will timeout.
// Reusing the properties bundle URL as a workaround
const INVALID_IMAGE_URI = PROPERTIES_URL;
const ERROR_MESSAGE = Services.strings
.createBundle(PROPERTIES_URL)
.GetStringFromName("styleinspector.copyImageDataUrlError");
add_task(function*() {
const PAGE_CONTENT = [
"<style type=\"text/css\">",
" .valid-background {",
" background-image: url(" + TEST_DATA_URI + ");",
" }",
" .invalid-background {",
" background-image: url(" + INVALID_IMAGE_URI + ");",
" }",
"</style>",
"<div class=\"valid-background\">Valid background image</div>",
"<div class=\"invalid-background\">Invalid background image</div>"
].join("\n");
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(PAGE_CONTENT));
yield startTest();
});
function* startTest() {
info("Opening rule view");
let ruleViewData = yield openRuleView();
info("Test valid background image URL in rule view");
yield testCopyImageDataUrlToClipboard(ruleViewData, ".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in rue view");
yield testCopyImageDataUrlToClipboard(ruleViewData, ".invalid-background", ERROR_MESSAGE);
info("Opening computed view");
let computedViewData = yield openComputedView();
info("Test valid background image URL in computed view");
yield testCopyImageDataUrlToClipboard(computedViewData, ".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in computed view");
yield testCopyImageDataUrlToClipboard(computedViewData, ".invalid-background", ERROR_MESSAGE);
}
function* testCopyImageDataUrlToClipboard({view, inspector}, selector, expected) {
info("Select node in inspector panel");
yield selectNode(selector, inspector);
info("Retrieve background-image link for selected node in current styleinspector view");
let property = getBackgroundImageProperty(view, selector);
let imageLink = property.valueSpan.querySelector(".theme-link");
ok(imageLink, "Background-image link element found");
info("Simulate right click on the background-image URL");
let popup = once(view._contextmenu, "popupshown");
// Cannot rely on synthesizeMouseAtCenter here. The image URL can be displayed on several lines.
// A click simulated at the exact center may click between the lines and miss the target
// Instead, using the top-left corner of first client rect, with an offset of 2 pixels.
let rect = imageLink.getClientRects()[0];
let x = rect.left + 2;
let y = rect.top + 2;
EventUtils.synthesizeMouseAtPoint(x, y, {button: 2, type: "contextmenu"}, getViewWindow(view));
yield popup;
info("Context menu is displayed");
ok(!view.menuitemCopyImageDataUrl.hidden, "\"Copy Image Data-URL\" menu entry is displayed");
info("Click Copy Data URI and wait for clipboard");
yield waitForClipboard(() => view.menuitemCopyImageDataUrl.click(), expected);
info("Hide context menu");
view._contextmenu.hidePopup();
}
function getBackgroundImageProperty(view, selector) {
let isRuleView = view instanceof CssRuleView;
if (isRuleView) {
return getRuleViewProperty(view, selector, "background-image");
} else {
return getComputedViewProperty(view, "background-image");
}
}
/**
* Function that returns the window for a given view.
*/
function getViewWindow(view) {
let viewDocument = view.styleDocument ? view.styleDocument : view.doc;
return viewDocument.defaultView;
}

View File

@ -36,7 +36,9 @@ add_task(function*() {
info("Faking a mousemove on a transform property");
({valueSpan} = getRuleViewProperty(rView, "body", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized");
let h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
@ -57,7 +59,9 @@ add_task(function*() {
info("Faking a mousemove on a transform property");
({valueSpan} = getComputedViewProperty(cView, "transform"));
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized");
h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");

View File

@ -39,53 +39,64 @@ add_task(function*() {
this.nodeFront = nodeFront;
this.isShown = true;
this.nbOfTimesShown ++;
return promise.resolve(true);
},
hide: function() {
this.nodeFront = null;
this.isShown = false;
return promise.resolve();
}
};
// Inject the mock highlighter in the rule-view
rView.highlighters.promises[TYPE] = {
then: function(cb) {
cb(HighlighterFront);
}
};
let hs = rView.highlighters;
hs.promises[TYPE] = promise.resolve(HighlighterFront);
let {valueSpan} = getRuleViewProperty(rView, "body", "transform");
info("Checking that the HighlighterFront's show/hide methods are called");
rView.highlighters._onMouseMove({target: valueSpan});
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(HighlighterFront.isShown, "The highlighter is shown");
rView.highlighters._onMouseLeave();
let onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseLeave();
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
info("Checking that hovering several times over the same property doesn't" +
" show the highlighter several times");
let nb = HighlighterFront.nbOfTimesShown;
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nbOfTimesShown, nb + 1, "The highlighter was shown once");
rView.highlighters._onMouseMove({target: valueSpan});
rView.highlighters._onMouseMove({target: valueSpan});
hs._onMouseMove({target: valueSpan});
hs._onMouseMove({target: valueSpan});
is(HighlighterFront.nbOfTimesShown, nb + 1,
"The highlighter was shown once, after several mousemove");
info("Checking that the right NodeFront reference is passed");
yield selectNode("html", inspector);
({valueSpan} = getRuleViewProperty(rView, "html", "transform"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "HTML",
"The right NodeFront is passed to the highlighter (1)");
yield selectNode("body", inspector);
({valueSpan} = getRuleViewProperty(rView, "body", "transform"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "BODY",
"The right NodeFront is passed to the highlighter (2)");
info("Checking that the highlighter gets hidden when hovering a non-transform property");
({valueSpan} = getRuleViewProperty(rView, "body", "color"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseMove({target: valueSpan});
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
});

View File

@ -55,7 +55,9 @@ add_task(function*() {
info("Faking a mousemove on the now unoverriden property");
({valueSpan} = getRuleViewProperty(rView, "div", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized now");
let h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");

View File

@ -431,7 +431,7 @@ if test -z "$RUSTC" -a -n "$MOZ_RUST"; then
To compile rust language sources, you must have 'rustc' in your path.
See http://www.rust-lang.org/ for more information.])
fi
if test -n "$MOZ_RUST" -a -z "$_RUSTC_MAJOR_VERSION" -o \
if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \
"$_RUSTC_MAJOR_VERSION" -lt 1; then
AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old.
To compile rust language sources please install at least

View File

@ -1369,9 +1369,10 @@ private:
(this->*(HandleNtf[index]))(aHeader, aPDU);
}
};
const int BluetoothDaemonCoreModule::MAX_NUM_CLIENTS = 1;
//
// Protocol handling
//
@ -1421,8 +1422,6 @@ private:
//
// - |HandleSvc|,
//
const int BluetoothDaemonCoreModule::MAX_NUM_CLIENTS = 1;
// which is called by |BluetoothDaemonProtcol| to hand over received
// PDUs into a module.
//
@ -1624,117 +1623,6 @@ BluetoothDaemonProtocol::FetchUserData(const BluetoothDaemonPDUHeader& aHeader)
return userData;
}
//
// Listen socket
//
class BluetoothDaemonListenSocket final : public ipc::ListenSocket
{
public:
BluetoothDaemonListenSocket(BluetoothDaemonInterface* aInterface);
// Connection state
//
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
BluetoothDaemonInterface* mInterface;
};
BluetoothDaemonListenSocket::BluetoothDaemonListenSocket(
BluetoothDaemonInterface* aInterface)
: mInterface(aInterface)
{ }
void
BluetoothDaemonListenSocket::OnConnectSuccess()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnConnectSuccess(BluetoothDaemonInterface::LISTEN_SOCKET);
}
void
BluetoothDaemonListenSocket::OnConnectError()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnConnectError(BluetoothDaemonInterface::LISTEN_SOCKET);
}
void
BluetoothDaemonListenSocket::OnDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnDisconnect(BluetoothDaemonInterface::LISTEN_SOCKET);
}
//
// Channels
//
class BluetoothDaemonChannel final : public BluetoothDaemonConnection
{
public:
BluetoothDaemonChannel(BluetoothDaemonInterface* aInterface,
BluetoothDaemonInterface::Channel aChannel,
BluetoothDaemonPDUConsumer* aConsumer);
// SocketBase
//
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
BluetoothDaemonInterface* mInterface;
BluetoothDaemonInterface::Channel mChannel;
};
BluetoothDaemonChannel::BluetoothDaemonChannel(
BluetoothDaemonInterface* aInterface,
BluetoothDaemonInterface::Channel aChannel,
BluetoothDaemonPDUConsumer* aConsumer)
: BluetoothDaemonConnection(aConsumer)
, mInterface(aInterface)
, mChannel(aChannel)
{ }
void
BluetoothDaemonChannel::OnConnectSuccess()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnConnectSuccess(mChannel);
}
void
BluetoothDaemonChannel::OnConnectError()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnConnectError(mChannel);
}
void
BluetoothDaemonChannel::OnDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInterface);
mInterface->OnDisconnect(mChannel);
}
//
// Interface
//
@ -1863,149 +1751,6 @@ private:
bool mRegisteredSocketModule;
};
void
BluetoothDaemonInterface::OnConnectSuccess(enum Channel aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aChannel) {
case LISTEN_SOCKET: {
// Init, step 2: Start Bluetooth daemon */
nsCString value("bluetoothd:-a ");
value.Append(mListenSocketName);
if (NS_WARN_IF(property_set("ctl.start", value.get()) < 0)) {
OnConnectError(CMD_CHANNEL);
}
/*
* If Bluetooth daemon is not running, retry to start it later.
*
* This condition happens when when we restart Bluetooth daemon
* immediately after it crashed, as the daemon state remains 'stopping'
* instead of 'stopped'. Due to the limitation of property service,
* hereby add delay. See Bug 1143925 Comment 41.
*/
if (!IsDaemonRunning()) {
MessageLoop::current()->PostDelayedTask(FROM_HERE,
new StartDaemonTask(this, value), sRetryInterval);
}
}
break;
case CMD_CHANNEL:
// Init, step 3: Listen for notification channel...
if (!mNtfChannel) {
mNtfChannel = new BluetoothDaemonChannel(this, NTF_CHANNEL, mProtocol);
} else if (
NS_WARN_IF(mNtfChannel->GetConnectionStatus() == SOCKET_CONNECTED)) {
/* Notification channel should not be open; let's close it. */
mNtfChannel->Close();
}
if (NS_FAILED(mListenSocket->Listen(mNtfChannel))) {
OnConnectError(NTF_CHANNEL);
}
break;
case NTF_CHANNEL: {
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
// Init, step 4: Register Core module
nsresult rv = mProtocol->RegisterModuleCmd(
0x01, 0x00, BluetoothDaemonCoreModule::MAX_NUM_CLIENTS,
new InitResultHandler(this, res));
if (NS_FAILED(rv) && res) {
DispatchError(res, STATUS_FAIL);
}
}
break;
}
}
void
BluetoothDaemonInterface::OnConnectError(enum Channel aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aChannel) {
case NTF_CHANNEL:
// Close command channel
mCmdChannel->Close();
case CMD_CHANNEL:
// Stop daemon and close listen socket
unused << NS_WARN_IF(property_set("ctl.stop", "bluetoothd"));
mListenSocket->Close();
case LISTEN_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
// Signal error to caller
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
if (res) {
DispatchError(res, STATUS_FAIL);
}
}
break;
}
}
/*
* Three cases for restarting:
* a) during startup
* b) during regular service
* c) during shutdown
* For (a)/(c) cases, mResultHandlerQ contains an element, but case (b)
* mResultHandlerQ shall be empty. The following procedure to recover from crashed
* consists of several steps for case (b).
* 1) Close listen socket.
* 2) Wait for all sockets disconnected and inform BluetoothServiceBluedroid to
* perform the regular stop bluetooth procedure.
* 3) When stop bluetooth procedures complete, fire
* AdapterStateChangedNotification to cleanup all necessary data members and
* deinit ProfileManagers.
* 4) After all resources cleanup, call |StartBluetooth|
*/
void
BluetoothDaemonInterface::OnDisconnect(enum Channel aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
switch (aChannel) {
case CMD_CHANNEL:
// We don't have to do anything here. Step 4 is triggered
// by the daemon.
break;
case NTF_CHANNEL:
// Cleanup, step 4 (Recovery, step 1): Close listen socket
mListenSocket->Close();
break;
case LISTEN_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
// Cleanup, step 5: Signal success to caller
if (res) {
res->Cleanup();
}
}
break;
}
/* For recovery make sure all sockets disconnected, in order to avoid
* the remaining disconnects interfere with the restart procedure.
*/
if (sNotificationHandler && mResultHandlerQ.IsEmpty()) {
if (mListenSocket->GetConnectionStatus() == SOCKET_DISCONNECTED &&
mCmdChannel->GetConnectionStatus() == SOCKET_DISCONNECTED &&
mNtfChannel->GetConnectionStatus() == SOCKET_DISCONNECTED) {
// Assume daemon crashed during regular service; notify
// BluetoothServiceBluedroid to prepare restart-daemon procedure
sNotificationHandler->BackendErrorNotification(true);
sNotificationHandler = nullptr;
}
}
}
nsresult
BluetoothDaemonInterface::CreateRandomAddressString(
const nsACString& aPrefix, unsigned long aPostfixLength,
@ -2098,13 +1843,13 @@ BluetoothDaemonInterface::Init(
}
if (!mListenSocket) {
mListenSocket = new BluetoothDaemonListenSocket(this);
mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
}
// Init, step 1: Listen for command channel... */
if (!mCmdChannel) {
mCmdChannel = new BluetoothDaemonChannel(this, CMD_CHANNEL, mProtocol);
mCmdChannel = new BluetoothDaemonConnection(mProtocol, this, CMD_CHANNEL);
} else if (
NS_WARN_IF(mCmdChannel->GetConnectionStatus() == SOCKET_CONNECTED)) {
// Command channel should not be open; let's close it.
@ -2556,4 +2301,149 @@ BluetoothDaemonInterface::GetBluetoothGattInterface()
return nullptr;
}
// |BluetoothDaemonConnectionConsumer|, |ListenSocketConsumer|
void
BluetoothDaemonInterface::OnConnectSuccess(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aIndex) {
case LISTEN_SOCKET: {
// Init, step 2: Start Bluetooth daemon */
nsCString value("bluetoothd:-a ");
value.Append(mListenSocketName);
if (NS_WARN_IF(property_set("ctl.start", value.get()) < 0)) {
OnConnectError(CMD_CHANNEL);
}
/*
* If Bluetooth daemon is not running, retry to start it later.
*
* This condition happens when when we restart Bluetooth daemon
* immediately after it crashed, as the daemon state remains 'stopping'
* instead of 'stopped'. Due to the limitation of property service,
* hereby add delay. See Bug 1143925 Comment 41.
*/
if (!IsDaemonRunning()) {
MessageLoop::current()->PostDelayedTask(FROM_HERE,
new StartDaemonTask(this, value), sRetryInterval);
}
}
break;
case CMD_CHANNEL:
// Init, step 3: Listen for notification channel...
if (!mNtfChannel) {
mNtfChannel = new BluetoothDaemonConnection(mProtocol, this, NTF_CHANNEL);
} else if (
NS_WARN_IF(mNtfChannel->GetConnectionStatus() == SOCKET_CONNECTED)) {
/* Notification channel should not be open; let's close it. */
mNtfChannel->Close();
}
if (NS_FAILED(mListenSocket->Listen(mNtfChannel))) {
OnConnectError(NTF_CHANNEL);
}
break;
case NTF_CHANNEL: {
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
// Init, step 4: Register Core module
nsresult rv = mProtocol->RegisterModuleCmd(
0x01, 0x00, BluetoothDaemonCoreModule::MAX_NUM_CLIENTS,
new InitResultHandler(this, res));
if (NS_FAILED(rv) && res) {
DispatchError(res, STATUS_FAIL);
}
}
break;
}
}
void
BluetoothDaemonInterface::OnConnectError(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
switch (aIndex) {
case NTF_CHANNEL:
// Close command channel
mCmdChannel->Close();
case CMD_CHANNEL:
// Stop daemon and close listen socket
unused << NS_WARN_IF(property_set("ctl.stop", "bluetoothd"));
mListenSocket->Close();
case LISTEN_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
// Signal error to caller
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
if (res) {
DispatchError(res, STATUS_FAIL);
}
}
break;
}
}
/*
* Three cases for restarting:
* a) during startup
* b) during regular service
* c) during shutdown
* For (a)/(c) cases, mResultHandlerQ contains an element, but case (b)
* mResultHandlerQ shall be empty. The following procedure to recover from crashed
* consists of several steps for case (b).
* 1) Close listen socket.
* 2) Wait for all sockets disconnected and inform BluetoothServiceBluedroid to
* perform the regular stop bluetooth procedure.
* 3) When stop bluetooth procedures complete, fire
* AdapterStateChangedNotification to cleanup all necessary data members and
* deinit ProfileManagers.
* 4) After all resources cleanup, call |StartBluetooth|
*/
void
BluetoothDaemonInterface::OnDisconnect(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
switch (aIndex) {
case CMD_CHANNEL:
// We don't have to do anything here. Step 4 is triggered
// by the daemon.
break;
case NTF_CHANNEL:
// Cleanup, step 4 (Recovery, step 1): Close listen socket
mListenSocket->Close();
break;
case LISTEN_SOCKET:
if (!mResultHandlerQ.IsEmpty()) {
nsRefPtr<BluetoothResultHandler> res = mResultHandlerQ.ElementAt(0);
mResultHandlerQ.RemoveElementAt(0);
// Cleanup, step 5: Signal success to caller
if (res) {
res->Cleanup();
}
}
break;
}
/* For recovery make sure all sockets disconnected, in order to avoid
* the remaining disconnects interfere with the restart procedure.
*/
if (sNotificationHandler && mResultHandlerQ.IsEmpty()) {
if (mListenSocket->GetConnectionStatus() == SOCKET_DISCONNECTED &&
mCmdChannel->GetConnectionStatus() == SOCKET_DISCONNECTED &&
mNtfChannel->GetConnectionStatus() == SOCKET_DISCONNECTED) {
// Assume daemon crashed during regular service; notify
// BluetoothServiceBluedroid to prepare restart-daemon procedure
sNotificationHandler->BackendErrorNotification(true);
sNotificationHandler = nullptr;
}
}
}
END_BLUETOOTH_NAMESPACE

View File

@ -8,26 +8,36 @@
#define mozilla_dom_bluetooth_bluedroid_bluetoothdaemoninterface_h__
#include "BluetoothInterface.h"
#include "mozilla/ipc/BluetoothDaemonConnectionConsumer.h"
#include "mozilla/ipc/ListenSocketConsumer.h"
namespace mozilla {
namespace ipc {
class BluetoothDaemonConnection;
class ListenSocket;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDaemonListenSocket;
class BluetoothDaemonChannel;
class BluetoothDaemonA2dpInterface;
class BluetoothDaemonAvrcpInterface;
class BluetoothDaemonHandsfreeInterface;
class BluetoothDaemonProtocol;
class BluetoothDaemonSocketInterface;
class BluetoothDaemonInterface final : public BluetoothInterface
class BluetoothDaemonInterface final
: public BluetoothInterface
, public mozilla::ipc::BluetoothDaemonConnectionConsumer
, public mozilla::ipc::ListenSocketConsumer
{
public:
class CleanupResultHandler;
class InitResultHandler;
class StartDaemonTask;
friend class BluetoothDaemonListenSocket;
friend class BluetoothDaemonChannel;
friend class CleanupResultHandler;
friend class InitResultHandler;
friend class StartDaemonTask;
@ -128,22 +138,25 @@ protected:
BluetoothDaemonInterface();
~BluetoothDaemonInterface();
void OnConnectSuccess(enum Channel aChannel);
void OnConnectError(enum Channel aChannel);
void OnDisconnect(enum Channel aChannel);
nsresult CreateRandomAddressString(const nsACString& aPrefix,
unsigned long aPostfixLength,
nsACString& aAddress);
// Methods for |BluetoothDaemonConnectionConsumer| and |ListenSocketConsumer|
//
void OnConnectSuccess(int aIndex) override;
void OnConnectError(int aIndex) override;
void OnDisconnect(int aIndex) override;
private:
void DispatchError(BluetoothResultHandler* aRes, BluetoothStatus aStatus);
void DispatchError(BluetoothResultHandler* aRes, nsresult aRv);
nsCString mListenSocketName;
nsRefPtr<BluetoothDaemonListenSocket> mListenSocket;
nsRefPtr<BluetoothDaemonChannel> mCmdChannel;
nsRefPtr<BluetoothDaemonChannel> mNtfChannel;
nsRefPtr<mozilla::ipc::ListenSocket> mListenSocket;
nsRefPtr<mozilla::ipc::BluetoothDaemonConnection> mCmdChannel;
nsRefPtr<mozilla::ipc::BluetoothDaemonConnection> mNtfChannel;
nsAutoPtr<BluetoothDaemonProtocol> mProtocol;
nsTArray<nsRefPtr<BluetoothResultHandler> > mResultHandlerQ;

View File

@ -95,6 +95,10 @@ MobileMessageCallback::~MobileMessageCallback()
nsresult
MobileMessageCallback::NotifySuccess(JS::Handle<JS::Value> aResult, bool aAsync)
{
if (NS_WARN_IF(!mDOMRequest->GetOwner())) {
return NS_ERROR_FAILURE;
}
if (aAsync) {
nsCOMPtr<nsIDOMRequestService> rs =
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
@ -126,6 +130,10 @@ MobileMessageCallback::NotifySuccess(nsISupports *aMessage, bool aAsync)
nsresult
MobileMessageCallback::NotifyError(int32_t aError, DOMError *aDetailedError, bool aAsync)
{
if (NS_WARN_IF(!mDOMRequest->GetOwner())) {
return NS_ERROR_FAILURE;
}
if (aAsync) {
NS_ASSERTION(!aDetailedError,
"No Support to FireDetailedErrorAsync() in nsIDOMRequestService!");
@ -156,18 +164,23 @@ MobileMessageCallback::NotifyMessageSent(nsISupports *aMessage)
NS_IMETHODIMP
MobileMessageCallback::NotifySendMessageFailed(int32_t aError, nsISupports *aMessage)
{
nsCOMPtr<nsPIDOMWindow> window = mDOMRequest->GetOwner();
if (NS_WARN_IF(!window)) {
return NS_ERROR_FAILURE;
}
nsRefPtr<DOMMobileMessageError> domMobileMessageError;
if (aMessage) {
nsAutoString errorStr = ConvertErrorCodeToErrorString(aError);
nsCOMPtr<nsIDOMMozSmsMessage> smsMsg = do_QueryInterface(aMessage);
if (smsMsg) {
domMobileMessageError =
new DOMMobileMessageError(mDOMRequest->GetOwner(), errorStr, smsMsg);
new DOMMobileMessageError(window, errorStr, smsMsg);
}
else {
nsCOMPtr<nsIDOMMozMmsMessage> mmsMsg = do_QueryInterface(aMessage);
domMobileMessageError =
new DOMMobileMessageError(mDOMRequest->GetOwner(), errorStr, mmsMsg);
new DOMMobileMessageError(window, errorStr, mmsMsg);
}
NS_ASSERTION(domMobileMessageError, "Invalid DOMMobileMessageError!");
}

View File

@ -128,7 +128,13 @@ MobileMessageManager::GetSegmentInfoForText(const nsAString& aText,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(request);
nsresult rv = smsService->GetSegmentInfoForText(aText, msgCallback);
@ -147,7 +153,13 @@ MobileMessageManager::Send(nsISmsService* aSmsService,
const nsAString& aText,
ErrorResult& aRv)
{
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(request);
@ -248,8 +260,14 @@ MobileMessageManager::SendMMS(const MmsParameters& aParams,
}
}
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
if (NS_WARN_IF(!jsapi.Init(window))) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
@ -261,7 +279,7 @@ MobileMessageManager::SendMMS(const MmsParameters& aParams,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
rv = mmsService->Send(serviceId, val, msgCallback);
if (NS_FAILED(rv)) {
@ -283,7 +301,13 @@ MobileMessageManager::GetMessage(int32_t aId,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
nsresult rv = dbService->GetMessageMoz(aId, msgCallback);
if (NS_FAILED(rv)) {
@ -306,7 +330,13 @@ MobileMessageManager::Delete(int32_t* aIdArray,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(request);
@ -454,8 +484,14 @@ MobileMessageManager::GetMessages(const MobileMessageFilter& aFilter,
return nullptr;
}
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
cursorCallback->mDOMCursor =
new MobileMessageCursor(GetOwner(), continueCallback);
new MobileMessageCursor(window, continueCallback);
nsRefPtr<DOMCursor> cursor(cursorCallback->mDOMCursor);
return cursor.forget();
@ -474,7 +510,13 @@ MobileMessageManager::MarkMessageRead(int32_t aId,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
nsresult rv = dbService->MarkMessageRead(aId, aValue, aSendReadReport,
msgCallback);
@ -507,8 +549,14 @@ MobileMessageManager::GetThreads(ErrorResult& aRv)
return nullptr;
}
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
cursorCallback->mDOMCursor =
new MobileMessageCursor(GetOwner(), continueCallback);
new MobileMessageCursor(window, continueCallback);
nsRefPtr<DOMCursor> cursor(cursorCallback->mDOMCursor);
return cursor.forget();
@ -524,7 +572,13 @@ MobileMessageManager::RetrieveMMS(int32_t aId,
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
nsresult rv = mmsService->Retrieve(aId, msgCallback);
@ -692,7 +746,13 @@ MobileMessageManager::GetSmscAddress(const Optional<uint32_t>& aServiceId,
}
}
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(window);
nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
rv = smsService->GetSmscAddress(serviceId, msgCallback);
if (NS_FAILED(rv)) {
@ -727,7 +787,13 @@ MobileMessageManager::SetSmscAddress(const SmscAddress& aSmscAddress,
}
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;

View File

@ -32,6 +32,7 @@ const kSmsSentObserverTopic = "sms-sent";
const kSmsFailedObserverTopic = "sms-failed";
const kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
const kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
const kSmsDeletedObserverTopic = "sms-deleted";
const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
const DOM_MOBILE_MESSAGE_DELIVERY_SENDING = "sending";
@ -75,6 +76,21 @@ XPCOMUtils.defineLazyGetter(this, "gWAP", function() {
return ns;
});
XPCOMUtils.defineLazyGetter(this, "gSmsSendingSchedulars", function() {
return {
_schedulars: [],
getSchedularByServiceId: function(aServiceId) {
let schedular = this._schedulars[aServiceId];
if (!schedular) {
schedular = this._schedulars[aServiceId] =
new SmsSendingSchedular(aServiceId);
}
return schedular;
}
};
});
XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
"@mozilla.org/cellbroadcast/cellbroadcastservice;1",
"nsIGonkCellBroadcastService");
@ -273,11 +289,82 @@ SmsService.prototype = {
return index;
},
_notifySendingError: function(aErrorCode, aSendingMessage, aSilent, aRequest) {
if (aSilent || aErrorCode === Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR) {
// There is no way to modify nsIDOMMozSmsMessage attributes as they
// are read only so we just create a new sms instance to send along
// with the notification.
aRequest.notifySendMessageFailed(aErrorCode,
gMobileMessageService.createSmsMessage(aSendingMessage.id,
aSendingMessage.threadId,
aSendingMessage.iccId,
DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
aSendingMessage.sender,
aSendingMessage.receiver,
aSendingMessage.body,
aSendingMessage.messageClass,
aSendingMessage.timestamp,
0,
0,
aSendingMessage.read));
if (!aSilent) {
Services.obs.notifyObservers(aSendingMessage, kSmsFailedObserverTopic, null);
}
return;
}
gMobileMessageDatabaseService
.setMessageDeliveryByMessageId(aSendingMessage.id,
null,
DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
null,
(aRv, aDomMessage) => {
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
this._broadcastSmsSystemMessage(
Ci.nsISmsMessenger.NOTIFICATION_TYPE_SENT_FAILED, aDomMessage);
aRequest.notifySendMessageFailed(aErrorCode, aDomMessage);
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
});
},
/**
* Schedule the sending request.
*/
_scheduleSending: function(aServiceId, aDomMessage, aSilent, aOptions, aRequest) {
gSmsSendingSchedulars.getSchedularByServiceId(aServiceId)
.schedule({
messageId: aDomMessage.id,
onSend: () => {
if (DEBUG) debug("onSend: " + aDomMessage.id);
this._sendToTheAir(aServiceId,
aDomMessage,
aSilent,
aOptions,
aRequest);
},
onCancel: (aErrorCode) => {
if (DEBUG) debug("onCancel: " + aErrorCode);
this._notifySendingError(aErrorCode, aDomMessage, aSilent, aRequest);
}
});
},
/**
* Send a SMS message to the modem.
*/
_sendToTheAir: function(aServiceId, aDomMessage, aSilent, aOptions, aRequest) {
// Keep current SMS message info for sent/delivered notifications
let sentMessage = aDomMessage;
let requestStatusReport = aOptions.requestStatusReport;
// Retry count for GECKO_ERROR_SMS_SEND_FAIL_RETRY
if (!aOptions.retryCount) {
aOptions.retryCount = 0;
}
gRadioInterfaces[aServiceId].sendWorkerMessage("sendSMS",
aOptions,
(aResponse) => {
@ -288,43 +375,18 @@ SmsService.prototype = {
error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
} else if (aResponse.errorMsg === RIL.GECKO_ERROR_FDN_CHECK_FAILURE) {
error = Ci.nsIMobileMessageCallback.FDN_CHECK_ERROR;
} else if (aResponse.errorMsg === RIL.GECKO_ERROR_SMS_SEND_FAIL_RETRY &&
aOptions.retryCount < RIL.SMS_RETRY_MAX) {
aOptions.retryCount++;
this._scheduleSending(aServiceId,
aDomMessage,
aSilent,
aOptions,
aRequest);
return;
}
if (aSilent) {
// There is no way to modify nsIDOMMozSmsMessage attributes as they
// are read only so we just create a new sms instance to send along
// with the notification.
aRequest.notifySendMessageFailed(
error,
gMobileMessageService.createSmsMessage(sentMessage.id,
sentMessage.threadId,
sentMessage.iccId,
DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
sentMessage.sender,
sentMessage.receiver,
sentMessage.body,
sentMessage.messageClass,
sentMessage.timestamp,
0,
0,
sentMessage.read));
return false;
}
gMobileMessageDatabaseService
.setMessageDeliveryByMessageId(aDomMessage.id,
null,
DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
null,
(aRv, aDomMessage) => {
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
this._broadcastSmsSystemMessage(
Ci.nsISmsMessenger.NOTIFICATION_TYPE_SENT_FAILED, aDomMessage);
aRequest.notifySendMessageFailed(error, aDomMessage);
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
});
this._notifySendingError(error, sentMessage, aSilent, aRequest);
return false;
} // End of send failure.
@ -906,28 +968,12 @@ SmsService.prototype = {
errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
}
if (errorCode) {
if (aSilent) {
aRequest.notifySendMessageFailed(errorCode, aSendingMessage);
return;
}
gMobileMessageDatabaseService
.setMessageDeliveryByMessageId(aSendingMessage.id,
null,
DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
null,
(aRv, aDomMessage) => {
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
this._broadcastSmsSystemMessage(
Ci.nsISmsMessenger.NOTIFICATION_TYPE_SENT_FAILED, aDomMessage);
aRequest.notifySendMessageFailed(errorCode, aDomMessage);
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
});
this._notifySendingError(errorCode, aSendingMessage, aSilent, aRequest);
return;
}
this._sendToTheAir(aServiceId, aSendingMessage, aSilent, options, aRequest);
this._scheduleSending(aServiceId, aSendingMessage, aSilent, options,
aRequest);
}; // End of |saveSendingMessageCallback|.
// Don't save message into DB for silent SMS.
@ -1147,4 +1193,171 @@ function getEnabledGsmTableTuplesFromMcc() {
return tuples;
};
function SmsSendingSchedular(aServiceId) {
this._serviceId = aServiceId;
this._queue = [];
Services.obs.addObserver(this, kSmsDeletedObserverTopic, false);
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
SmsSendingSchedular.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionListener]),
_serviceId: 0,
_isObservingMoboConn: false,
_queue: null,
/**
* Ensure the handler is listening on MobileConnection events.
*/
_ensureMoboConnObserverRegistration: function() {
if (!this._isObservingMoboConn) {
let connection =
gMobileConnectionService.getItemByServiceId(this._serviceId);
if (connection) {
connection.registerListener(this);
this._isObservingMoboConn = true;
}
}
},
/**
* Ensure the handler is not listening on MobileConnection events.
*/
_ensureMoboConnObserverUnregistration: function() {
if (this._isObservingMoboConn) {
let connection =
gMobileConnectionService.getItemByServiceId(this._serviceId);
if (connection) {
connection.unregisterListener(this);
this._isObservingMoboConn = false;
}
}
},
/**
* Schedule a sending request.
*
* @param aSendingRequest
* An object with the following properties:
* - messageId
* The messageId in MobileMessageDB.
* - onSend
* The callback to invoke to trigger a sending or retry.
* - onCancel
* The callback to invoke when the request is canceled and won't
* retry.
*/
schedule: function(aSendingRequest) {
if (aSendingRequest) {
this._ensureMoboConnObserverRegistration();
this._queue.push(aSendingRequest)
// Keep the queue in order to guarantee the sending order matches user
// expectation.
this._queue.sort(function(a, b) {
return a.messageId - b.messageId;
});
}
this.send();
},
/**
* Send all requests in the queue if voice connection is available.
*/
send: function() {
let connection =
gMobileConnectionService.getItemByServiceId(this._servicdeId);
// If the voice connection is temporarily unavailable, pend the request.
let voiceInfo = connection && connection.voice;
let voiceConnected = voiceInfo && voiceInfo.connected;
if (!voiceConnected) {
if (DEBUG) {
debug("Voice connection is temporarily unavailable. Skip sending.");
}
return;
}
let snapshot = this._queue;
this._queue = [];
let req;
while ((req = snapshot.shift())) {
req.onSend();
}
// The sending / retry could fail and being re-scheduled immediately.
// Only unregister listener when the queue is empty after retries.
if (this._queue.length === 0) {
this._ensureMoboConnObserverUnregistration();
}
},
/**
* nsIObserver interface.
*/
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case kSmsDeletedObserverTopic:
if (DEBUG) {
debug("Observe " + kSmsDeletedObserverTopic + ": " +
JSON.stringify(aSubject));
}
if (aSubject && aSubject.deletedMessageIds) {
for (let id of aSubject.deletedMessageIds) {
for (let i = 0; i < this._queue.length; i++) {
if (this._queue[i].messageId === id) {
if (DEBUG) debug("Deleting message with id=" + id);
this._queue.splice(i, 1)[0].onCancel(
Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
}
}
}
}
break;
case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
this._ensureMoboConnObserverUnregistration();
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
Services.obs.removeObserver(this, kSmsDeletedObserverTopic);
// Cancel all pending requests and clear the queue.
for (let req of this._queue) {
req.onCancel(Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR);
}
this._queue = [];
break;
}
},
/**
* nsIMobileConnectionListener implementation.
*/
notifyVoiceChanged: function() {
let connection = gMobileConnectionService.getItemByServiceId(this._serviceId);
let voiceInfo = connection && connection.voice;
let voiceConnected = voiceInfo && voiceInfo.connected;
if (voiceConnected) {
if (DEBUG) {
debug("Voice connected. Resend pending requests.");
}
this.send();
}
},
// Unused nsIMobileConnectionListener methods.
notifyDataChanged: function() {},
notifyDataError: function(message) {},
notifyCFStateChanged: function(action, reason, number, timeSeconds, serviceClass) {},
notifyEmergencyCbModeChanged: function(active, timeoutMs) {},
notifyOtaStatusChanged: function(status) {},
notifyRadioStateChanged: function() {},
notifyClirModeChanged: function(mode) {},
notifyLastKnownNetworkChanged: function() {},
notifyLastKnownHomeNetworkChanged: function() {},
notifyNetworkSelectionModeChanged: function() {}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsService]);

View File

@ -77,6 +77,49 @@ function ensureMobileMessage() {
return deferred.promise;
}
/**
* Push required permissions and test if |navigator.mozMobileConnections| exists.
* Resolve if it does, reject otherwise.
*
* Fulfill params:
* manager -- an reference to navigator.mozMobileConnections.
*
* Reject params: (none)
*
* @param aServiceId [optional]
* A numeric DSDS service id. Default: 0.
*
* @return A deferred promise.
*/
let mobileConnection;
function ensureMobileConnection(aServiceId) {
return new Promise(function(resolve, reject) {
let permissions = [{
"type": "mobileconnection",
"allow": 1,
"context": document,
}];
SpecialPowers.pushPermissions(permissions, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
let serviceId = aServiceId || 0;
mobileConnection = window.navigator.mozMobileConnections[serviceId];
if (mobileConnection) {
log("navigator.mozMobileConnections[" + serviceId + "] is instance of " +
mobileConnection.constructor);
} else {
log("navigator.mozMobileConnections[" + serviceId + "] is undefined");
}
if (mobileConnection instanceof MozMobileConnection) {
resolve(mobileConnection);
} else {
reject();
}
});
});
}
/**
* Wait for one named MobileMessageManager event.
*
@ -501,6 +544,28 @@ function sendMultipleRawSmsToEmulatorAndWait(aPdus) {
return Promise.all(promises);
}
/**
* Set voice state and wait for state change.
*
* @param aState
* "unregistered", "searching", "denied", "roaming", or "home".
*
* @return A deferred promise.
*/
function setEmulatorVoiceStateAndWait(aState) {
let promises = [];
promises.push(new Promise(function(resolve, reject) {
mobileConnection.addEventListener("voicechange", function onevent(aEvent) {
log("voicechange: connected=" + mobileConnection.voice.connected);
mobileConnection.removeEventListener("voicechange", onevent);
resolve(aEvent);
})
}));
promises.push(runEmulatorCmdSafe("gsm voice " + aState));
return Promise.all(promises);
}
/**
* Create a new array of id attribute of input messages.
*

View File

@ -53,3 +53,4 @@ qemu = true
[test_decode_spanish_fallback.js]
[test_update_gsm_nl_on_mcc_chanages.js]
[test_set_smsc_address.js]
[test_outgoing_unstable_voice_connection.js]

View File

@ -0,0 +1,138 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const RECEIVER = "15555215555";
// The Book of Mozilla, 15:1
const MESSAGES = [
"The twins of Mammon quarrelled.",
"Their warring plunged the world into a new darkness, and the beast abhorred the darkness.",
"So it began to move swiftly, and grew more powerful, and went forth and multiplied.",
"And the beasts brought fire and light to the darkness."
];
/**
* Send messages and verify that corresponding "sending" events are received.
*
* @return an array of message IDs.
*/
function sendMessagesAndVerifySending() {
return new Promise(function(resolve, reject) {
try {
let eventCount = 0;
let now = Date.now();
let messageIds = [];
manager.addEventListener("sending", function onevent(aEvent) {
log("onsending event received.");
let message = aEvent.message;
let expectedBody = MESSAGES[eventCount++];
messageIds.push(message.id);
is(message.delivery, "sending", "message.delivery");
is(message.deliveryStatus, "pending", "message.deliveryStatus");
is(message.body, expectedBody, "message.body: expected '" + expectedBody
+ "'' but got '" + message.body + "'");
// timestamp is in seconds.
ok(Math.floor(message.timestamp / 1000) >= Math.floor(now / 1000),
"expected " + message.timestamp + " >= " + now);
// resolve when all messages are appeared "sending"
if (eventCount == MESSAGES.length) {
manager.removeEventListener("sending", onevent);
resolve(messageIds);
}
});
// send messages
for (let body of MESSAGES) {
manager.send(RECEIVER, body);
}
} catch (err) {
log("Error: " + err);
reject(err);
}
});
}
/**
* Turn on voice connection, delete specified messages, and verify all the
* corresponding "sent" / "failed" events are received.
*
* @param aMessageIdsToDelete
* An array of message ids to delete.
* @param aExpectedSentMessages
* Expected successfully sent messages, ordered.
* @param aExpectedFailures
* Expected number of failures.
*/
function turnOnVoiceDeleteMessagesAndVerify(aMessageIdsToDelete,
aExpectedSentMessages, aExpectedFailures) {
let promises = [];
// Wait for "sent" and "failed" events.
promises.push(new Promise(function(resolve, reject) {
try {
let sentEventCount = 0;
let failedEventCount = 0;
let onSentHandler = function(aEvent) {
log("onsent event received.");
let message = aEvent.message;
let expectedBody = aExpectedSentMessages[sentEventCount++];
is(message.delivery, "sent", "message.delivery");
is(message.receiver, RECEIVER, "message.receiver");
is(message.body, expectedBody, "message.body: expected '" + expectedBody
+ "'' but got '" + message.body + "'");
tryResolve();
}
let onFailedHandler = function(aEvent) {
log("onfailed event received.");
failedEventCount++;
tryResolve();
}
let tryResolve = function() {
log("sentEventCount=" + sentEventCount + "; failedEventCount=" + failedEventCount);
if (sentEventCount === aExpectedSentMessages.length &&
failedEventCount === aExpectedFailures) {
manager.removeEventListener("sent", onSentHandler);
manager.removeEventListener("failed", onFailedHandler);
resolve();
}
}
manager.addEventListener("sent", onSentHandler);
manager.addEventListener("failed", onFailedHandler);
} catch (err) {
log("Error: " + err);
reject(err);
}
}));
// Delete messages with given ids.
promises.push(deleteMessagesById(aMessageIdsToDelete));
// wait for 3 seconds and turn on voice connection.
promises.push(new Promise(function(resolve, reject) {
setTimeout(() => resolve(), 3000);
}).then(() => setEmulatorVoiceStateAndWait("on")));
return Promise.all(promises);
}
startTestCommon(function testCaseMain() {
return pushPrefEnv({ set: [['dom.sms.requestStatusReport', true]] })
.then(() => ensureMobileConnection())
.then(() => setEmulatorVoiceStateAndWait("unregistered"))
.then(() => sendMessagesAndVerifySending())
// Delete the first message and wait for result.
.then((aMessageIds) => turnOnVoiceDeleteMessagesAndVerify([aMessageIds[0]],
MESSAGES.slice().splice(1, MESSAGES.length - 1), 1));
});

View File

@ -27,6 +27,36 @@ using namespace android;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace {
class SendNfcSocketDataTask final : public nsRunnable
{
public:
SendNfcSocketDataTask(StreamSocket* aSocket, UnixSocketRawData* aRawData)
: mSocket(aSocket)
, mRawData(aRawData)
{ }
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mSocket || mSocket->GetConnectionStatus() != SOCKET_CONNECTED) {
// Probably shutting down.
return NS_OK;
}
mSocket->SendSocketData(mRawData.forget());
return NS_OK;
}
private:
nsRefPtr<StreamSocket> mSocket;
nsAutoPtr<UnixSocketRawData> mRawData;
};
} // anonymous namespace
namespace mozilla {
static NfcService* gNfcService;
@ -41,11 +71,15 @@ assertIsNfcServiceThread()
}
// Runnable used to call Marshall on the NFC thread.
class NfcCommandRunnable : public nsRunnable
class NfcCommandRunnable final : public nsRunnable
{
public:
NfcCommandRunnable(NfcMessageHandler* aHandler, NfcConsumer* aConsumer, CommandOptions aOptions)
: mHandler(aHandler), mConsumer(aConsumer), mOptions(aOptions)
NfcCommandRunnable(NfcMessageHandler* aHandler,
NfcService* aService,
CommandOptions aOptions)
: mHandler(aHandler)
, mService(aService)
, mOptions(aOptions)
{
MOZ_ASSERT(NS_IsMainThread());
}
@ -60,13 +94,13 @@ public:
parcel.setDataPosition(0);
uint32_t sizeBE = htonl(parcel.dataSize() - sizeof(int));
parcel.writeInt32(sizeBE);
mConsumer->PostToNfcDaemon(parcel.data(), parcel.dataSize());
mService->PostToNfcDaemon(parcel.data(), parcel.dataSize());
return NS_OK;
}
private:
NfcMessageHandler* mHandler;
NfcConsumer* mConsumer;
NfcService* mService;
CommandOptions mOptions;
};
@ -307,15 +341,15 @@ NfcService::Start(nsINfcGonkEventListener* aListener)
mListener = aListener;
mHandler = new NfcMessageHandler();
mConsumer = new NfcConsumer(this);
mStreamSocket = new StreamSocket(this, STREAM_SOCKET);
mListenSocketName = BASE_SOCKET_NAME;
mListenSocket = new NfcListenSocket(this);
mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
nsresult rv = mListenSocket->Listen(new NfcConnector(mListenSocketName),
mConsumer);
mStreamSocket);
if (NS_FAILED(rv)) {
mConsumer = nullptr;
mStreamSocket = nullptr;
return rv;
}
@ -324,8 +358,8 @@ NfcService::Start(nsINfcGonkEventListener* aListener)
NS_WARNING("Can't create Nfc worker thread.");
mListenSocket->Close();
mListenSocket = nullptr;
mConsumer->Shutdown();
mConsumer = nullptr;
mStreamSocket->Close();
mStreamSocket = nullptr;
return NS_ERROR_FAILURE;
}
@ -344,13 +378,24 @@ NfcService::Shutdown()
mListenSocket->Close();
mListenSocket = nullptr;
mConsumer->Shutdown();
mConsumer = nullptr;
mStreamSocket->Close();
mStreamSocket = nullptr;
return NS_OK;
}
bool
NfcService::PostToNfcDaemon(const uint8_t* aData, size_t aSize)
{
MOZ_ASSERT(!NS_IsMainThread());
UnixSocketRawData* raw = new UnixSocketRawData(aData, aSize);
nsRefPtr<SendNfcSocketDataTask> task =
new SendNfcSocketDataTask(mStreamSocket, raw);
NS_DispatchToMainThread(task);
return true;
}
NS_IMETHODIMP
NfcService::SendCommand(JS::HandleValue aOptions, JSContext* aCx)
{
@ -365,7 +410,8 @@ NfcService::SendCommand(JS::HandleValue aOptions, JSContext* aCx)
// Dispatch the command to the NFC thread.
CommandOptions commandOptions(options);
nsCOMPtr<nsIRunnable> runnable = new NfcCommandRunnable(mHandler, mConsumer, commandOptions);
nsCOMPtr<nsIRunnable> runnable = new NfcCommandRunnable(mHandler, this,
commandOptions);
mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
return NS_OK;
}
@ -385,8 +431,11 @@ NfcService::DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions)
mListener->OnEvent(val);
}
// |StreamSocketConsumer|, |ListenSocketConsumer|
void
NfcService::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
NfcService::ReceiveSocketData(
int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer)
{
MOZ_ASSERT(mHandler);
nsCOMPtr<nsIRunnable> runnable =
@ -395,11 +444,11 @@ NfcService::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
}
void
NfcService::OnConnectSuccess(enum SocketType aSocketType)
NfcService::OnConnectSuccess(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
switch (aSocketType) {
switch (aIndex) {
case LISTEN_SOCKET: {
nsCString value("nfcd:-a ");
value.Append(mListenSocketName);
@ -415,7 +464,7 @@ NfcService::OnConnectSuccess(enum SocketType aSocketType)
}
void
NfcService::OnConnectError(enum SocketType aSocketType)
NfcService::OnConnectError(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
@ -423,7 +472,7 @@ NfcService::OnConnectError(enum SocketType aSocketType)
}
void
NfcService::OnDisconnect(enum SocketType aSocketType)
NfcService::OnDisconnect(int aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
}

View File

@ -7,8 +7,10 @@
#ifndef NfcService_h
#define NfcService_h
#include "mozilla/ipc/Nfc.h"
#include "mozilla/ipc/SocketBase.h"
#include <mozilla/ipc/ListenSocket.h>
#include "mozilla/ipc/ListenSocketConsumer.h"
#include <mozilla/ipc/StreamSocket.h>
#include "mozilla/ipc/StreamSocketConsumer.h"
#include "nsCOMPtr.h"
#include "nsINfcService.h"
#include "NfcMessageHandler.h"
@ -20,8 +22,10 @@ namespace dom {
class NfcEventOptions;
} // namespace dom
class NfcService final : public nsINfcService,
public mozilla::ipc::NfcSocketListener
class NfcService final
: public nsINfcService
, public mozilla::ipc::StreamSocketConsumer
, public mozilla::ipc::ListenSocketConsumer
{
public:
NS_DECL_ISUPPORTS
@ -31,25 +35,34 @@ public:
void DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions);
virtual void ReceiveSocketData(
nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
virtual void OnConnectSuccess(enum SocketType aSocketType) override;
virtual void OnConnectError(enum SocketType aSocketType) override;
virtual void OnDisconnect(enum SocketType aSocketType) override;
bool PostToNfcDaemon(const uint8_t* aData, size_t aSize);
nsCOMPtr<nsIThread> GetThread() {
return mThread;
}
// Methods for |StreamSocketConsumer| and |ListenSocketConsumer|
//
void ReceiveSocketData(
int aIndex, nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
void OnConnectSuccess(int aIndex) override;
void OnConnectError(int aIndex) override;
void OnDisconnect(int aIndex) override;
private:
enum SocketType {
LISTEN_SOCKET,
STREAM_SOCKET
};
NfcService();
~NfcService();
nsCOMPtr<nsIThread> mThread;
nsCOMPtr<nsINfcGonkEventListener> mListener;
nsRefPtr<mozilla::ipc::NfcListenSocket> mListenSocket;
nsRefPtr<mozilla::ipc::NfcConsumer> mConsumer;
nsRefPtr<mozilla::ipc::ListenSocket> mListenSocket;
nsRefPtr<mozilla::ipc::StreamSocket> mStreamSocket;
nsAutoPtr<NfcMessageHandler> mHandler;
nsCString mListenSocketName;
};

View File

@ -1457,10 +1457,6 @@ RilObject.prototype = {
options.langIndex = options.langIndex || PDU_NL_IDENTIFIER_DEFAULT;
options.langShiftIndex = options.langShiftIndex || PDU_NL_IDENTIFIER_DEFAULT;
if (!options.retryCount) {
options.retryCount = 0;
}
if (!options.segmentSeq) {
// Fist segment to send
options.segmentSeq = 1;
@ -4024,15 +4020,6 @@ RilObject.prototype = {
options.errorMsg);
}
if (options.errorMsg === GECKO_ERROR_SMS_SEND_FAIL_RETRY &&
options.retryCount < SMS_RETRY_MAX) {
options.retryCount++;
// TODO: bug 736702 TP-MR, retry interval, retry timeout
this.sendSMS(options);
return;
}
// Fallback to default error handling if it meets max retry count.
this.sendChromeMessage({
rilMessageType: options.rilMessageType,
rilMessageToken: options.rilMessageToken,

View File

@ -10,6 +10,7 @@
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include "mozilla/ipc/BluetoothDaemonConnectionConsumer.h"
#include "mozilla/ipc/DataSocket.h"
#include "mozilla/ipc/UnixSocketConnector.h"
#include "mozilla/ipc/UnixSocketWatcher.h"
@ -433,8 +434,12 @@ BluetoothDaemonConnectionIO::ShutdownOnIOThread()
//
BluetoothDaemonConnection::BluetoothDaemonConnection(
BluetoothDaemonPDUConsumer* aConsumer)
: mConsumer(aConsumer)
BluetoothDaemonPDUConsumer* aPDUConsumer,
BluetoothDaemonConnectionConsumer* aConsumer,
int aIndex)
: mPDUConsumer(aPDUConsumer)
, mConsumer(aConsumer)
, mIndex(aIndex)
, mIO(nullptr)
{
MOZ_ASSERT(mConsumer);
@ -461,7 +466,7 @@ BluetoothDaemonConnection::PrepareAccept(UnixSocketConnector* aConnector,
mIO = new BluetoothDaemonConnectionIO(
XRE_GetIOMessageLoop(), -1, UnixSocketWatcher::SOCKET_IS_CONNECTING,
this, mConsumer);
this, mPDUConsumer);
aIO = mIO;
return NS_OK;
@ -500,5 +505,29 @@ BluetoothDaemonConnection::Close()
NotifyDisconnect();
}
void
BluetoothDaemonConnection::OnConnectSuccess()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectSuccess(mIndex);
}
void
BluetoothDaemonConnection::OnConnectError()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectError(mIndex);
}
void
BluetoothDaemonConnection::OnDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnDisconnect(mIndex);
}
}
}

View File

@ -15,6 +15,7 @@
namespace mozilla {
namespace ipc {
class BluetoothDaemonConnectionConsumer;
class BluetoothDaemonConnectionIO;
class BluetoothDaemonPDUConsumer;
@ -113,7 +114,9 @@ protected:
class BluetoothDaemonConnection : public ConnectionOrientedSocket
{
public:
BluetoothDaemonConnection(BluetoothDaemonPDUConsumer* aConsumer);
BluetoothDaemonConnection(BluetoothDaemonPDUConsumer* aPDUConsumer,
BluetoothDaemonConnectionConsumer* aConsumer,
int aIndex);
virtual ~BluetoothDaemonConnection();
// Methods for |ConnectionOrientedSocket|
@ -131,9 +134,14 @@ public:
//
void Close() override;
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
BluetoothDaemonPDUConsumer* mConsumer;
BluetoothDaemonPDUConsumer* mPDUConsumer;
BluetoothDaemonConnectionConsumer* mConsumer;
int mIndex;
BluetoothDaemonConnectionIO* mIO;
};

View File

@ -0,0 +1,23 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#include "BluetoothDaemonConnectionConsumer.h"
namespace mozilla {
namespace ipc {
//
// BluetoothDaemonConnectionConsumer
//
BluetoothDaemonConnectionConsumer::BluetoothDaemonConnectionConsumer()
{ }
BluetoothDaemonConnectionConsumer::~BluetoothDaemonConnectionConsumer()
{ }
}
}

View File

@ -0,0 +1,54 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#ifndef mozilla_ipc_BluetoothDaemonConnectionConsumer_h
#define mozilla_ipc_BluetoothDaemonConnectionConsumer_h
#include "mozilla/Attributes.h"
#include "mozilla/FileUtils.h"
#include "mozilla/ipc/ConnectionOrientedSocket.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace ipc {
/*
* |BluetoothDaemonConnectionConsumer| handles socket events.
*/
class BluetoothDaemonConnectionConsumer
{
public:
/**
* Callback for socket success. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnConnectSuccess(int aIndex) = 0;
/**
* Callback for socket errors. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnConnectError(int aIndex) = 0;
/**
* Callback for socket disconnect. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnDisconnect(int aIndex) = 0;
protected:
BluetoothDaemonConnectionConsumer();
virtual ~BluetoothDaemonConnectionConsumer();
};
}
}
#endif

View File

@ -5,11 +5,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.ipc += [
'BluetoothDaemonConnection.h'
'BluetoothDaemonConnection.h',
'BluetoothDaemonConnectionConsumer.h'
]
SOURCES += [
'BluetoothDaemonConnection.cpp'
'BluetoothDaemonConnection.cpp',
'BluetoothDaemonConnectionConsumer.cpp'
]
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -672,79 +672,6 @@ checkPermission(uid_t uid)
return false;
}
//
// KeyStore::ListenSocket
//
KeyStore::ListenSocket::ListenSocket(KeyStore* aKeyStore)
: mKeyStore(aKeyStore)
{
MOZ_ASSERT(mKeyStore);
MOZ_COUNT_CTOR(KeyStore::ListenSocket);
}
void
KeyStore::ListenSocket::OnConnectSuccess()
{
mKeyStore->OnConnectSuccess(LISTEN_SOCKET);
MOZ_COUNT_DTOR(KeyStore::ListenSocket);
}
void
KeyStore::ListenSocket::OnConnectError()
{
mKeyStore->OnConnectError(LISTEN_SOCKET);
}
void
KeyStore::ListenSocket::OnDisconnect()
{
mKeyStore->OnDisconnect(LISTEN_SOCKET);
}
//
// KeyStore::StreamSocket
//
KeyStore::StreamSocket::StreamSocket(KeyStore* aKeyStore)
: mKeyStore(aKeyStore)
{
MOZ_ASSERT(mKeyStore);
MOZ_COUNT_CTOR(KeyStore::StreamSocket);
}
KeyStore::StreamSocket::~StreamSocket()
{
MOZ_COUNT_DTOR(KeyStore::StreamSocket);
}
void
KeyStore::StreamSocket::OnConnectSuccess()
{
mKeyStore->OnConnectSuccess(STREAM_SOCKET);
}
void
KeyStore::StreamSocket::OnConnectError()
{
mKeyStore->OnConnectError(STREAM_SOCKET);
}
void
KeyStore::StreamSocket::OnDisconnect()
{
mKeyStore->OnDisconnect(STREAM_SOCKET);
}
void
KeyStore::StreamSocket::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
{
mKeyStore->ReceiveSocketData(aBuffer);
}
//
// KeyStore
//
@ -795,12 +722,12 @@ KeyStore::Listen()
if (mStreamSocket) {
mStreamSocket->Close();
} else {
mStreamSocket = new StreamSocket(this);
mStreamSocket = new StreamSocket(this, STREAM_SOCKET);
}
if (!mListenSocket) {
// We only ever allocate one |ListenSocket|...
mListenSocket = new ListenSocket(this);
mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
mListenSocket->Listen(new KeyStoreConnector(KEYSTORE_ALLOWED_USERS),
mStreamSocket);
} else {
@ -952,8 +879,10 @@ KeyStore::SendData(const uint8_t *aData, int aLength)
mStreamSocket->SendSocketData(data);
}
// |StreamSocketConsumer|, |ListenSocketConsumer|
void
KeyStore::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aMessage)
KeyStore::ReceiveSocketData(int aIndex, nsAutoPtr<UnixSocketBuffer>& aMessage)
{
MOZ_ASSERT(NS_IsMainThread());
@ -1013,34 +942,34 @@ KeyStore::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aMessage)
}
void
KeyStore::OnConnectSuccess(SocketType aSocketType)
KeyStore::OnConnectSuccess(int aIndex)
{
if (aSocketType == STREAM_SOCKET) {
if (aIndex == STREAM_SOCKET) {
mShutdown = false;
}
}
void
KeyStore::OnConnectError(SocketType aSocketType)
KeyStore::OnConnectError(int aIndex)
{
if (mShutdown) {
return;
}
if (aSocketType == STREAM_SOCKET) {
if (aIndex == STREAM_SOCKET) {
// Stream socket error; start listening again
Listen();
}
}
void
KeyStore::OnDisconnect(SocketType aSocketType)
KeyStore::OnDisconnect(int aIndex)
{
if (mShutdown) {
return;
}
switch (aSocketType) {
switch (aIndex) {
case LISTEN_SOCKET:
// Listen socket disconnected; start anew.
mListenSocket = nullptr;

View File

@ -11,7 +11,9 @@
#include <sys/un.h>
#include "cert.h"
#include "mozilla/ipc/ListenSocket.h"
#include "mozilla/ipc/ListenSocketConsumer.h"
#include "mozilla/ipc/StreamSocket.h"
#include "mozilla/ipc/StreamSocketConsumer.h"
#include "nsNSSShutDown.h"
namespace mozilla {
@ -78,7 +80,10 @@ typedef enum {
STATE_PROCESSING
} ProtocolHandlerState;
class KeyStore final : public nsNSSShutDownObject
class KeyStore final
: public StreamSocketConsumer
, public ListenSocketConsumer
, public nsNSSShutDownObject
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KeyStore)
@ -96,50 +101,8 @@ private:
STREAM_SOCKET
};
class ListenSocket final : public mozilla::ipc::ListenSocket
{
public:
ListenSocket(KeyStore* aKeyStore);
ListenSocket();
// SocketBase
//
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
KeyStore* mKeyStore;
};
class StreamSocket final : public mozilla::ipc::StreamSocket
{
public:
StreamSocket(KeyStore* aKeyStore);
~StreamSocket();
// SocketConsumerBase
//
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) override;
private:
KeyStore* mKeyStore;
};
~KeyStore();
void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aMessage);
void OnConnectSuccess(enum SocketType aSocketType);
void OnConnectError(enum SocketType aSocketType);
void OnDisconnect(enum SocketType aSocketType);
struct {
ProtocolHandlerState state;
uint8_t command;
@ -157,6 +120,15 @@ private:
void SendResponse(ResponseCode response);
void SendData(const uint8_t *data, int length);
// Methods for |StreamSocketConsumer|
//
void ReceiveSocketData(int aIndex,
nsAutoPtr<UnixSocketBuffer>& aMessage) override;
void OnConnectSuccess(int aIndex) override;
void OnConnectError(int aIndex) override;
void OnDisconnect(int aIndex) override;
bool mShutdown;
nsRefPtr<ListenSocket> mListenSocket;

View File

@ -1,165 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=2 ts=8 et ft=cpp: */
/* 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/. */
/* Copyright © 2013, Deutsche Telekom, Inc. */
#include "mozilla/ipc/Nfc.h"
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#undef CHROMIUM_LOG
#if (defined(MOZ_WIDGET_GONK) && defined(DEBUG))
#include <android/log.h>
#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
#else
#define CHROMIUM_LOG(args...)
#endif
#include "jsfriendapi.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ipc/NfcConnector.h"
#include "nsThreadUtils.h" // For NS_IsMainThread.
using namespace mozilla::ipc;
namespace {
class SendNfcSocketDataTask final : public nsRunnable
{
public:
SendNfcSocketDataTask(NfcConsumer* aConsumer, UnixSocketRawData* aRawData)
: mConsumer(aConsumer)
, mRawData(aRawData)
{ }
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mConsumer ||
mConsumer->GetConnectionStatus() != SOCKET_CONNECTED) {
// Probably shuting down.
return NS_OK;
}
mConsumer->SendSocketData(mRawData.forget());
return NS_OK;
}
private:
NfcConsumer* mConsumer;
nsAutoPtr<UnixSocketRawData> mRawData;
};
} // anonymous namespace
namespace mozilla {
namespace ipc {
//
// NfcListenSocket
//
NfcListenSocket::NfcListenSocket(NfcSocketListener* aListener)
: mListener(aListener)
{ }
void
NfcListenSocket::OnConnectSuccess()
{
if (mListener) {
mListener->OnConnectSuccess(NfcSocketListener::LISTEN_SOCKET);
}
}
void
NfcListenSocket::OnConnectError()
{
if (mListener) {
mListener->OnConnectError(NfcSocketListener::LISTEN_SOCKET);
}
}
void
NfcListenSocket::OnDisconnect()
{
if (mListener) {
mListener->OnDisconnect(NfcSocketListener::LISTEN_SOCKET);
}
}
//
// NfcConsumer
//
NfcConsumer::NfcConsumer(NfcSocketListener* aListener)
: mListener(aListener)
{ }
void
NfcConsumer::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
Close();
}
bool
NfcConsumer::PostToNfcDaemon(const uint8_t* aData, size_t aSize)
{
MOZ_ASSERT(!NS_IsMainThread());
UnixSocketRawData* raw = new UnixSocketRawData(aData, aSize);
nsRefPtr<SendNfcSocketDataTask> task = new SendNfcSocketDataTask(this, raw);
NS_DispatchToMainThread(task);
return true;
}
void
NfcConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
if (mListener) {
mListener->ReceiveSocketData(aBuffer);
}
}
void
NfcConsumer::OnConnectSuccess()
{
CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
if (mListener) {
mListener->OnConnectSuccess(NfcSocketListener::STREAM_SOCKET);
}
// Nothing to do here.
}
void
NfcConsumer::OnConnectError()
{
CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
if (mListener) {
mListener->OnConnectError(NfcSocketListener::STREAM_SOCKET);
}
}
void
NfcConsumer::OnDisconnect()
{
CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
if (mListener) {
mListener->OnDisconnect(NfcSocketListener::STREAM_SOCKET);
}
}
} // namespace ipc
} // namespace mozilla

View File

@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp: */
/* 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/. */
/* Copyright © 2013, Deutsche Telekom, Inc. */
#ifndef mozilla_ipc_Nfc_h
#define mozilla_ipc_Nfc_h 1
#include <mozilla/ipc/ListenSocket.h>
#include <mozilla/ipc/StreamSocket.h>
namespace mozilla {
namespace ipc {
class NfcSocketListener
{
public:
enum SocketType {
LISTEN_SOCKET,
STREAM_SOCKET
};
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aData) = 0;
virtual void OnConnectSuccess(enum SocketType aSocketType) = 0;
virtual void OnConnectError(enum SocketType aSocketType) = 0;
virtual void OnDisconnect(enum SocketType aSocketType) = 0;
};
class NfcListenSocket final : public mozilla::ipc::ListenSocket
{
public:
NfcListenSocket(NfcSocketListener* aListener);
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
NfcSocketListener* mListener;
};
class NfcConsumer final : public mozilla::ipc::StreamSocket
{
public:
NfcConsumer(NfcSocketListener* aListener);
void Shutdown();
bool PostToNfcDaemon(const uint8_t* aData, size_t aSize);
private:
void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) override;
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
NfcSocketListener* mListener;
};
} // namespace ipc
} // namepsace mozilla
#endif // mozilla_ipc_Nfc_h

View File

@ -5,11 +5,9 @@
if CONFIG['MOZ_NFC']:
EXPORTS.mozilla.ipc += [
'Nfc.h',
'NfcConnector.h',
]
SOURCES += [
'Nfc.cpp',
'NfcConnector.cpp',
]

View File

@ -32,7 +32,7 @@ namespace {
static const char RIL_SOCKET_NAME[] = "/dev/socket/rilproxy";
static nsTArray<nsRefPtr<mozilla::ipc::RilConsumer> > sRilConsumers;
static nsTArray<nsAutoPtr<mozilla::ipc::RilConsumer>> sRilConsumers;
class ConnectWorkerToRIL final : public WorkerTask
{
@ -53,15 +53,13 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
if (sRilConsumers.Length() <= mClientId ||
!sRilConsumers[mClientId] ||
sRilConsumers[mClientId]->GetConnectionStatus() != SOCKET_CONNECTED) {
// Probably shuting down.
if (sRilConsumers.Length() <= mClientId || !sRilConsumers[mClientId]) {
// Probably shutting down.
delete mRawData;
return NS_OK;
}
sRilConsumers[mClientId]->SendSocketData(mRawData);
sRilConsumers[mClientId]->Send(mRawData);
return NS_OK;
}
@ -203,7 +201,6 @@ namespace ipc {
RilConsumer::RilConsumer(unsigned long aClientId,
WorkerCrossThreadDispatcher* aDispatcher)
: mDispatcher(aDispatcher)
, mClientId(aClientId)
, mShutdown(false)
{
// Only append client id after RIL_SOCKET_NAME when it's not connected to
@ -217,7 +214,8 @@ RilConsumer::RilConsumer(unsigned long aClientId,
mAddress = addr_un.sun_path;
}
Connect(new RilConnector(mAddress, mClientId));
mSocket = new StreamSocket(this, aClientId);
mSocket->Connect(new RilConnector(mAddress, aClientId));
}
nsresult
@ -250,7 +248,7 @@ RilConsumer::Shutdown()
MOZ_ASSERT(NS_IsMainThread());
for (unsigned long i = 0; i < sRilConsumers.Length(); i++) {
nsRefPtr<RilConsumer>& instance = sRilConsumers[i];
nsAutoPtr<RilConsumer> instance(sRilConsumers[i]);
if (!instance) {
continue;
}
@ -262,36 +260,58 @@ RilConsumer::Shutdown()
}
void
RilConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
RilConsumer::Send(UnixSocketRawData* aRawData)
{
if (!mSocket || mSocket->GetConnectionStatus() != SOCKET_CONNECTED) {
// Probably shutting down.
delete aRawData;
return;
}
mSocket->SendSocketData(aRawData);
}
void
RilConsumer::Close()
{
mSocket->Close();
mSocket = nullptr;
}
// |StreamSocketConnector|
void
RilConsumer::ReceiveSocketData(int aIndex,
nsAutoPtr<UnixSocketBuffer>& aBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(mClientId, aBuffer.forget()));
nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(aIndex, aBuffer.forget()));
mDispatcher->PostTask(dre);
}
void
RilConsumer::OnConnectSuccess()
RilConsumer::OnConnectSuccess(int aIndex)
{
// Nothing to do here.
CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
}
void
RilConsumer::OnConnectError()
RilConsumer::OnConnectError(int aIndex)
{
CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
Close();
}
void
RilConsumer::OnDisconnect()
RilConsumer::OnDisconnect(int aIndex)
{
CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
if (mShutdown) {
return;
}
Connect(new RilConnector(mAddress, mClientId), GetSuggestedConnectDelayMs());
mSocket->Connect(new RilConnector(mAddress, aIndex),
mSocket->GetSuggestedConnectDelayMs());
}
} // namespace ipc

View File

@ -9,11 +9,12 @@
#include <mozilla/dom/workers/Workers.h>
#include <mozilla/ipc/StreamSocket.h>
#include <mozilla/ipc/StreamSocketConsumer.h>
namespace mozilla {
namespace ipc {
class RilConsumer final : public mozilla::ipc::StreamSocket
class RilConsumer final : public StreamSocketConsumer
{
public:
static nsresult Register(
@ -21,18 +22,25 @@ public:
mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
static void Shutdown();
void Send(UnixSocketRawData* aRawData);
private:
RilConsumer(unsigned long aClientId,
mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) override;
void Close();
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
// Methods for |StreamSocketConsumer|
//
void ReceiveSocketData(int aIndex,
nsAutoPtr<UnixSocketBuffer>& aBuffer) override;
void OnConnectSuccess(int aIndex) override;
void OnConnectError(int aIndex) override;
void OnDisconnect(int aIndex) override;
nsRefPtr<StreamSocket> mSocket;
nsRefPtr<mozilla::dom::workers::WorkerCrossThreadDispatcher> mDispatcher;
unsigned long mClientId;
nsCString mAddress;
bool mShutdown;
};

View File

@ -8,6 +8,7 @@
#include <fcntl.h>
#include "ConnectionOrientedSocket.h"
#include "DataSocket.h"
#include "ListenSocketConsumer.h"
#include "mozilla/RefPtr.h"
#include "nsXULAppAPI.h"
#include "UnixSocketConnector.h"
@ -291,9 +292,13 @@ private:
// UnixSocketConsumer
//
ListenSocket::ListenSocket()
: mIO(nullptr)
{ }
ListenSocket::ListenSocket(ListenSocketConsumer* aConsumer, int aIndex)
: mConsumer(aConsumer)
, mIndex(aIndex)
, mIO(nullptr)
{
MOZ_ASSERT(mConsumer);
}
ListenSocket::~ListenSocket()
{
@ -376,5 +381,29 @@ ListenSocket::Close()
NotifyDisconnect();
}
void
ListenSocket::OnConnectSuccess()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectSuccess(mIndex);
}
void
ListenSocket::OnConnectError()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectError(mIndex);
}
void
ListenSocket::OnDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnDisconnect(mIndex);
}
} // namespace ipc
} // namespace mozilla

View File

@ -14,16 +14,17 @@ namespace mozilla {
namespace ipc {
class ConnectionOrientedSocket;
class ListenSocketConsumer;
class ListenSocketIO;
class UnixSocketConnector;
class ListenSocket : public SocketBase
class ListenSocket final : public SocketBase
{
protected:
virtual ~ListenSocket();
public:
ListenSocket();
ListenSocket(ListenSocketConsumer* aConsumer, int aIndex);
/**
* Starts a task on the socket that will try to accept a new connection
@ -52,8 +53,13 @@ public:
//
void Close() override;
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
private:
ListenSocketConsumer* mConsumer;
int mIndex;
ListenSocketIO* mIO;
};

View File

@ -0,0 +1,20 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#include "ListenSocketConsumer.h"
namespace mozilla {
namespace ipc {
//
// ListenSocketConsumer
//
ListenSocketConsumer::~ListenSocketConsumer()
{ }
}
}

View File

@ -0,0 +1,46 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#ifndef mozilla_ipc_listensocketconsumer_h
#define mozilla_ipc_listensocketconsumer_h
namespace mozilla {
namespace ipc {
/**
* |ListenSocketConsumer| handles socket events.
*/
class ListenSocketConsumer
{
public:
virtual ~ListenSocketConsumer();
/**
* Callback for socket success. Main-thread only.
*
* @param aIndex The index that has been given to the listening socket.
*/
virtual void OnConnectSuccess(int aIndex) = 0;
/**
* Callback for socket errors. Main-thread only.
*
* @param aIndex The index that has been given to the listening socket.
*/
virtual void OnConnectError(int aIndex) = 0;
/**
* Callback for socket disconnect. Main-thread only.
*
* @param aIndex The index that has been given to the listeing socket.
*/
virtual void OnDisconnect(int aIndex) = 0;
};
}
}
#endif

View File

@ -8,6 +8,7 @@
#include <fcntl.h>
#include "mozilla/RefPtr.h"
#include "nsXULAppAPI.h"
#include "StreamSocketConsumer.h"
#include "UnixSocketConnector.h"
static const size_t MAX_READ_SIZE = 1 << 16;
@ -499,15 +500,27 @@ public:
// StreamSocket
//
StreamSocket::StreamSocket()
: mIO(nullptr)
{ }
StreamSocket::StreamSocket(StreamSocketConsumer* aConsumer, int aIndex)
: mConsumer(aConsumer)
, mIndex(aIndex)
, mIO(nullptr)
{
MOZ_ASSERT(mConsumer);
}
StreamSocket::~StreamSocket()
{
MOZ_ASSERT(!mIO);
}
void
StreamSocket::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->ReceiveSocketData(mIndex, aBuffer);
}
nsresult
StreamSocket::Connect(UnixSocketConnector* aConnector,
int aDelayMs)
@ -586,5 +599,29 @@ StreamSocket::Close()
NotifyDisconnect();
}
void
StreamSocket::OnConnectSuccess()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectSuccess(mIndex);
}
void
StreamSocket::OnConnectError()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnConnectError(mIndex);
}
void
StreamSocket::OnDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mConsumer->OnDisconnect(mIndex);
}
} // namespace ipc
} // namespace mozilla

View File

@ -12,21 +12,21 @@
namespace mozilla {
namespace ipc {
class StreamSocketConsumer;
class StreamSocketIO;
class UnixSocketConnector;
class StreamSocket : public ConnectionOrientedSocket
class StreamSocket final : public ConnectionOrientedSocket
{
public:
StreamSocket();
StreamSocket(StreamSocketConsumer* aConsumer, int aIndex);
/**
* Method to be called whenever data is received. This is only called on the
* main thread.
* Method to be called whenever data is received. Main-thread only.
*
* @param aBuffer Data received from the socket.
*/
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) = 0;
void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer);
/**
* Starts a task on the socket that will try to connect to a socket in a
@ -53,11 +53,16 @@ public:
//
void Close() override;
void OnConnectSuccess() override;
void OnConnectError() override;
void OnDisconnect() override;
protected:
virtual ~StreamSocket();
private:
StreamSocketConsumer* mConsumer;
int mIndex;
StreamSocketIO* mIO;
};

View File

@ -0,0 +1,20 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#include "StreamSocketConsumer.h"
namespace mozilla {
namespace ipc {
//
// StreamSocketConsumer
//
StreamSocketConsumer::~StreamSocketConsumer()
{ }
}
}

View File

@ -0,0 +1,60 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#ifndef mozilla_ipc_streamsocketconsumer_h
#define mozilla_ipc_streamsocketconsumer_h
#include "nsAutoPtr.h"
namespace mozilla {
namespace ipc {
class UnixSocketBuffer;
/**
* |StreamSocketConsumer| handles socket events and received data.
*/
class StreamSocketConsumer
{
public:
/**
* Method to be called whenever data is received. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
* @param aBuffer Data received from the socket.
*/
virtual void ReceiveSocketData(int aIndex,
nsAutoPtr<UnixSocketBuffer>& aBuffer) = 0;
/**
* Callback for socket success. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnConnectSuccess(int aIndex) = 0;
/**
* Callback for socket errors. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnConnectError(int aIndex) = 0;
/**
* Callback for socket disconnect. Main-thread only.
*
* @param aIndex The index that has been given to the stream socket.
*/
virtual void OnDisconnect(int aIndex) = 0;
protected:
virtual ~StreamSocketConsumer();
};
}
}
#endif

View File

@ -8,8 +8,10 @@ EXPORTS.mozilla.ipc += [
'ConnectionOrientedSocket.h',
'DataSocket.h',
'ListenSocket.h',
'ListenSocketConsumer.h',
'SocketBase.h',
'StreamSocket.h',
'StreamSocketConsumer.h',
'UnixSocketConnector.h'
]
@ -17,8 +19,10 @@ SOURCES += [
'ConnectionOrientedSocket.cpp',
'DataSocket.cpp',
'ListenSocket.cpp',
'ListenSocketConsumer.cpp',
'SocketBase.cpp',
'StreamSocket.cpp',
'StreamSocketConsumer.cpp',
'UnixSocketConnector.cpp'
]

Some files were not shown because too many files have changed in this diff Show More