Merge m-c to inbound. a=merge
@ -1042,3 +1042,6 @@ pref("dom.mapped_arraybuffer.enabled", true);
|
||||
|
||||
// UDPSocket API
|
||||
pref("dom.udpsocket.enabled", true);
|
||||
|
||||
// Enable TV Manager API
|
||||
pref("dom.tv.enabled", true);
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -1,9 +1,8 @@
|
||||
[
|
||||
{
|
||||
"size": 120750384,
|
||||
"digest": "0e0a0b0dcca020e3283ce8deb33d0eed48fab16ef2fd919120bd7b5abba00713210be17f466d11bf77cca3c9e3b663805be61774476cc669f0a75736d901edfd",
|
||||
"size": 91247216,
|
||||
"digest": "2b4be549f98695488ea7288d9e7f8ac0fa45112bedefa485a6e016c4af73fa21bb6b3992beda516f268417207c5deb57afad3959d3b1fbd07d5269b3a6be6a27",
|
||||
"algorithm": "sha512",
|
||||
"filename": "backup-flame.tar.xz",
|
||||
"comment": "v188-1"
|
||||
"filename": "backup-flame.tar.xz"
|
||||
}
|
||||
]
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "ab5fb977047100ac985402e022d24da1dea68f24",
|
||||
"revision": "4830af61b34d751c57da15abfc2efcda571b12c0",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -15,7 +15,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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
|
||||
|
@ -17,7 +17,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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -197,6 +197,7 @@
|
||||
@BINPATH@/components/dom_telephony.xpt
|
||||
@BINPATH@/components/dom_threads.xpt
|
||||
@BINPATH@/components/dom_traversal.xpt
|
||||
@BINPATH@/components/dom_tv.xpt
|
||||
@BINPATH@/components/dom_views.xpt
|
||||
@BINPATH@/components/dom_voicemail.xpt
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
|
@ -1764,8 +1764,10 @@ pref("media.gmp-gmpopenh264.provider.enabled", true);
|
||||
|
||||
pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("browser.polaris.enabled", false);
|
||||
pref("privacy.trackingprotection.ui.enabled", false);
|
||||
#endif
|
||||
|
||||
// Temporary pref to allow printing in e10s windows on some platforms.
|
||||
#ifdef UNIX_BUT_NOT_MAC
|
||||
|
@ -250,6 +250,8 @@ let LoopCallsInternal = {
|
||||
respData.calls.forEach((callData) => {
|
||||
if (!this.callsData.inUse) {
|
||||
callData.sessionType = sessionType;
|
||||
// XXX Bug 1090209 will transiton into a better window id.
|
||||
callData.windowId = callData.callId;
|
||||
this._startCall(callData, "incoming");
|
||||
} else {
|
||||
this._returnBusy(callData);
|
||||
@ -277,7 +279,7 @@ let LoopCallsInternal = {
|
||||
null,
|
||||
// No title, let the page set that, to avoid flickering.
|
||||
"",
|
||||
"about:loopconversation#" + conversationType + "/" + callData.callId);
|
||||
"about:loopconversation#" + conversationType + "/" + callData.windowId);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -295,7 +297,7 @@ let LoopCallsInternal = {
|
||||
contact: contact,
|
||||
callType: callType,
|
||||
// XXX Really we shouldn't be using random numbers, bug 1090209 will fix this.
|
||||
callId: Math.floor((Math.random() * 100000000))
|
||||
windowId: Math.floor((Math.random() * 100000000))
|
||||
};
|
||||
|
||||
this._startCall(callData, "outgoing");
|
||||
@ -339,17 +341,17 @@ this.LoopCalls = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the callData for a specific loopCallId
|
||||
* Returns the callData for a specific conversation window id.
|
||||
*
|
||||
* The data was retrieved from the LoopServer via a GET/calls/<version> request
|
||||
* triggered by an incoming message from the LoopPushServer.
|
||||
*
|
||||
* @param {int} loopCallId
|
||||
* @param {Number} conversationWindowId
|
||||
* @return {callData} The callData or undefined if error.
|
||||
*/
|
||||
getCallData: function(loopCallId) {
|
||||
getCallData: function(conversationWindowId) {
|
||||
if (LoopCallsInternal.callsData.data &&
|
||||
LoopCallsInternal.callsData.data.callId == loopCallId) {
|
||||
LoopCallsInternal.callsData.data.windowId == conversationWindowId) {
|
||||
return LoopCallsInternal.callsData.data;
|
||||
} else {
|
||||
return undefined;
|
||||
@ -357,15 +359,15 @@ this.LoopCalls = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Releases the callData for a specific loopCallId
|
||||
* Releases the callData for a specific conversation window id.
|
||||
*
|
||||
* The result of this call will be a free call session slot.
|
||||
*
|
||||
* @param {int} loopCallId
|
||||
* @param {Number} conversationWindowId
|
||||
*/
|
||||
releaseCallData: function(loopCallId) {
|
||||
releaseCallData: function(conversationWindowId) {
|
||||
if (LoopCallsInternal.callsData.data &&
|
||||
LoopCallsInternal.callsData.data.callId == loopCallId) {
|
||||
LoopCallsInternal.callsData.data.windowId == conversationWindowId) {
|
||||
LoopCallsInternal.callsData.data = undefined;
|
||||
LoopCallsInternal.callsData.inUse = false;
|
||||
}
|
||||
|
@ -5,339 +5,222 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopService",
|
||||
"resource:///modules/loop/MozLoopService.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LOOP_SESSION_TYPE",
|
||||
"resource:///modules/loop/MozLoopService.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
||||
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
||||
const {MozLoopService, LOOP_SESSION_TYPE} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
return new EventEmitter();
|
||||
});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
|
||||
|
||||
let gRoomsListFetched = false;
|
||||
let gRooms = new Map();
|
||||
let gCallbacks = new Map();
|
||||
|
||||
/**
|
||||
* Callback used to indicate changes to rooms data on the LoopServer.
|
||||
*
|
||||
* @param {Object} version Version number assigned to this change set.
|
||||
* @param {Object} channelID Notification channel identifier.
|
||||
*
|
||||
*/
|
||||
const roomsPushNotification = function(version, channelID) {
|
||||
return LoopRoomsInternal.onNotification(version, channelID);
|
||||
};
|
||||
|
||||
let LoopRoomsInternal = {
|
||||
getAll: function(callback) {
|
||||
Task.spawn(function*() {
|
||||
yield MozLoopService.register();
|
||||
|
||||
if (gRoomsListFetched) {
|
||||
callback(null, [...gRooms.values()]);
|
||||
return;
|
||||
}
|
||||
// Fetch the rooms from the server.
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
let rooms = yield this.requestRoomList(sessionType);
|
||||
// Add each room to our in-memory Map using a locally unique
|
||||
// identifier.
|
||||
for (let room of rooms) {
|
||||
let id = MozLoopService.generateLocalID();
|
||||
room.localRoomId = id;
|
||||
// Next, request the detailed information for each room.
|
||||
// If the request fails the room data will not be added to the map.
|
||||
try {
|
||||
let details = yield this.requestRoomDetails(room.roomToken, sessionType);
|
||||
for (let attr in details) {
|
||||
room[attr] = details[attr]
|
||||
}
|
||||
delete room.currSize; //This attribute will be eliminated in the next revision.
|
||||
gRooms.set(id, room);
|
||||
}
|
||||
catch (error) {MozLoopService.log.warn(
|
||||
"failed GETing room details for roomToken = " + room.roomToken + ": ", error)}
|
||||
}
|
||||
callback(null, [...gRooms.values()]);
|
||||
return;
|
||||
}.bind(this)).catch((error) => {MozLoopService.log.error("getAll error:", error);
|
||||
callback(error)});
|
||||
return;
|
||||
},
|
||||
|
||||
getRoomData: function(localRoomId, callback) {
|
||||
if (gRooms.has(localRoomId)) {
|
||||
callback(null, gRooms.get(localRoomId));
|
||||
} else {
|
||||
callback(new Error("Room data not found or not fetched yet for room with ID " + localRoomId));
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Request list of all rooms associated with this account.
|
||||
*
|
||||
* @param {String} sessionType Indicates which hawkRequest endpoint to use.
|
||||
*
|
||||
* @returns {Promise} room list
|
||||
*/
|
||||
requestRoomList: function(sessionType) {
|
||||
return MozLoopService.hawkRequest(sessionType, "/rooms", "GET")
|
||||
.then(response => {
|
||||
let roomsList = JSON.parse(response.body);
|
||||
if (!Array.isArray(roomsList)) {
|
||||
// Force a reject in the returned promise.
|
||||
// To be caught by the caller using the returned Promise.
|
||||
throw new Error("Missing array of rooms in response.");
|
||||
}
|
||||
return roomsList;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Request information about a specific room from the server.
|
||||
*
|
||||
* @param {Object} token Room identifier returned from the LoopServer.
|
||||
* @param {String} sessionType Indicates which hawkRequest endpoint to use.
|
||||
*
|
||||
* @returns {Promise} room details
|
||||
*/
|
||||
requestRoomDetails: function(token, sessionType) {
|
||||
return MozLoopService.hawkRequest(sessionType, "/rooms/" + token, "GET")
|
||||
.then(response => JSON.parse(response.body));
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback used to indicate changes to rooms data on the LoopServer.
|
||||
*
|
||||
* @param {Object} version Version number assigned to this change set.
|
||||
* @param {Object} channelID Notification channel identifier.
|
||||
*
|
||||
*/
|
||||
onNotification: function(version, channelID) {
|
||||
return;
|
||||
},
|
||||
|
||||
createRoom: function(props, callback) {
|
||||
// Always create a basic room record and launch the window, attaching
|
||||
// the localRoomId. Later errors will be returned via the registered callback.
|
||||
let localRoomId = MozLoopService.generateLocalID((id) => {gRooms.has(id)})
|
||||
let room = {localRoomId : localRoomId};
|
||||
for (let prop in props) {
|
||||
room[prop] = props[prop]
|
||||
}
|
||||
|
||||
gRooms.set(localRoomId, room);
|
||||
this.addCallback(localRoomId, "RoomCreated", callback);
|
||||
MozLoopService.openChatWindow(null, "", "about:loopconversation#room/" + localRoomId);
|
||||
|
||||
if (!"roomName" in props ||
|
||||
!"expiresIn" in props ||
|
||||
!"roomOwner" in props ||
|
||||
!"maxSize" in props) {
|
||||
this.postCallback(localRoomId, "RoomCreated",
|
||||
new Error("missing required room create property"));
|
||||
return localRoomId;
|
||||
}
|
||||
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
|
||||
MozLoopService.hawkRequest(sessionType, "/rooms", "POST", props).then(
|
||||
(response) => {
|
||||
let data = JSON.parse(response.body);
|
||||
for (let attr in data) {
|
||||
room[attr] = data[attr]
|
||||
}
|
||||
delete room.expiresIn; //Do not keep this value - it is a request to the server
|
||||
this.postCallback(localRoomId, "RoomCreated", null, room);
|
||||
},
|
||||
(error) => {
|
||||
this.postCallback(localRoomId, "RoomCreated", error);
|
||||
});
|
||||
|
||||
return localRoomId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send an update to the callbacks registered for a specific localRoomId
|
||||
* for a callback type.
|
||||
*
|
||||
* The result set is always saved. Then each
|
||||
* callback function that has been registered when this function is
|
||||
* called will be called with the result set. Any new callback that
|
||||
* is regsitered via addCallback will receive a copy of the last
|
||||
* saved result set when registered. This allows the posting operation
|
||||
* to complete before the callback is registered in an asynchronous
|
||||
* operation.
|
||||
*
|
||||
* Callbacsk must be of the form:
|
||||
* function (error, success) {...}
|
||||
*
|
||||
* @param {String} localRoomId Local room identifier.
|
||||
* @param {String} callbackName callback type
|
||||
* @param {?Error} error result or null.
|
||||
* @param {?Object} success result if error argument is null.
|
||||
*/
|
||||
postCallback: function(localRoomId, callbackName, error, success) {
|
||||
let roomCallbacks = gCallbacks.get(localRoomId);
|
||||
if (!roomCallbacks) {
|
||||
// No callbacks have been registered or results posted for this room.
|
||||
// Initialize a record for this room and callbackName, saving the
|
||||
// result set.
|
||||
gCallbacks.set(localRoomId, new Map([[
|
||||
callbackName,
|
||||
{ callbackList: [], result: { error: error, success: success } }]]));
|
||||
return;
|
||||
}
|
||||
|
||||
let namedCallback = roomCallbacks.get(callbackName);
|
||||
// A callback of this name has not been registered.
|
||||
if (!namedCallback) {
|
||||
roomCallbacks.set(
|
||||
callbackName,
|
||||
{callbackList: [], result: {error: error, success: success}});
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the latest result set.
|
||||
namedCallback.result = {error: error, success: success};
|
||||
|
||||
// Call each registerd callback passing the new result posted.
|
||||
namedCallback.callbackList.forEach((callback) => {
|
||||
callback(error, success);
|
||||
});
|
||||
},
|
||||
|
||||
addCallback: function(localRoomId, callbackName, callback) {
|
||||
let roomCallbacks = gCallbacks.get(localRoomId);
|
||||
if (!roomCallbacks) {
|
||||
// No callbacks have been registered or results posted for this room.
|
||||
// Initialize a record for this room and callbackName.
|
||||
gCallbacks.set(localRoomId, new Map([[
|
||||
callbackName,
|
||||
{callbackList: [callback]}]]));
|
||||
return;
|
||||
}
|
||||
|
||||
let namedCallback = roomCallbacks.get(callbackName);
|
||||
// A callback of this name has not been registered.
|
||||
if (!namedCallback) {
|
||||
roomCallbacks.set(
|
||||
callbackName,
|
||||
{callbackList: [callback]});
|
||||
return;
|
||||
}
|
||||
|
||||
// Add this callback if not already in the array
|
||||
if (namedCallback.callbackList.indexOf(callback) >= 0) {
|
||||
return;
|
||||
}
|
||||
namedCallback.callbackList.push(callback);
|
||||
|
||||
// If a result has been posted for this callback
|
||||
// send it using this new callback function.
|
||||
let result = namedCallback.result;
|
||||
if (result) {
|
||||
callback(result.error, result.success);
|
||||
}
|
||||
},
|
||||
|
||||
deleteCallback: function(localRoomId, callbackName, callback) {
|
||||
let roomCallbacks = gCallbacks.get(localRoomId);
|
||||
if (!roomCallbacks) {
|
||||
return;
|
||||
}
|
||||
|
||||
let namedCallback = roomCallbacks.get(callbackName);
|
||||
if (!namedCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
let i = namedCallback.callbackList.indexOf(callback);
|
||||
if (i >= 0) {
|
||||
namedCallback.callbackList.splice(i, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
return LoopRoomsInternal.onNotification(version, channelID);
|
||||
};
|
||||
Object.freeze(LoopRoomsInternal);
|
||||
|
||||
// Since the LoopRoomsInternal.rooms map as defined below is a local cache of
|
||||
// room objects that are retrieved from the server, this is list may become out
|
||||
// of date. The Push server may notify us of this event, which will set the global
|
||||
// 'dirty' flag to TRUE.
|
||||
let gDirty = true;
|
||||
|
||||
/**
|
||||
* The LoopRooms class.
|
||||
* Extend a `target` object with the properties defined in `source`.
|
||||
*
|
||||
* @param {Object} target The target object to receive properties defined in `source`
|
||||
* @param {Object} source The source object to copy properties from
|
||||
*/
|
||||
const extend = function(target, source) {
|
||||
for (let key of Object.getOwnPropertyNames(source)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Rooms class.
|
||||
*
|
||||
* Each method that is a member of this class requires the last argument to be a
|
||||
* callback Function. MozLoopAPI will cause things to break if this invariant is
|
||||
* violated. You'll notice this as well in the documentation for each method.
|
||||
*/
|
||||
this.LoopRooms = {
|
||||
let LoopRoomsInternal = {
|
||||
rooms: new Map(),
|
||||
|
||||
/**
|
||||
* Fetch a list of rooms that the currently registered user is a member of.
|
||||
*
|
||||
* @param {String} [version] If set, we will fetch a list of changed rooms since
|
||||
* `version`. Optional.
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be the list of rooms, if it was fetched successfully.
|
||||
*/
|
||||
getAll: function(version = null, callback) {
|
||||
if (!callback) {
|
||||
callback = version;
|
||||
version = null;
|
||||
}
|
||||
|
||||
Task.spawn(function* () {
|
||||
yield MozLoopService.register();
|
||||
|
||||
if (!gDirty) {
|
||||
callback(null, [...this.rooms.values()]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the rooms from the server.
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
let url = "/rooms" + (version ? "?version=" + encodeURIComponent(version) : "");
|
||||
let response = yield MozLoopService.hawkRequest(sessionType, url, "GET");
|
||||
let roomsList = JSON.parse(response.body);
|
||||
if (!Array.isArray(roomsList)) {
|
||||
throw new Error("Missing array of rooms in response.");
|
||||
}
|
||||
|
||||
// Next, request the detailed information for each room. If the request
|
||||
// fails the room data will not be added to the map.
|
||||
for (let room of roomsList) {
|
||||
this.rooms.set(room.roomToken, room);
|
||||
yield LoopRooms.promise("get", room.roomToken);
|
||||
}
|
||||
|
||||
// Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
|
||||
gDirty = false;
|
||||
callback(null, [...this.rooms.values()]);
|
||||
}.bind(this)).catch(error => {
|
||||
callback(error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Request information about a specific room from the server. It will be
|
||||
* returned from the cache if it's already in it.
|
||||
*
|
||||
* @param {String} roomToken Room identifier
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be the list of rooms, if it was fetched successfully.
|
||||
*/
|
||||
get: function(roomToken, callback) {
|
||||
let room = this.rooms.has(roomToken) ? this.rooms.get(roomToken) : {};
|
||||
// Check if we need to make a request to the server to collect more room data.
|
||||
if (!room || gDirty || !("participants" in room)) {
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
MozLoopService.hawkRequest(sessionType, "/rooms/" + encodeURIComponent(roomToken), "GET")
|
||||
.then(response => {
|
||||
let eventName = ("roomToken" in room) ? "add" : "update";
|
||||
extend(room, JSON.parse(response.body));
|
||||
// Remove the `currSize` for posterity.
|
||||
if ("currSize" in room) {
|
||||
delete room.currSize;
|
||||
}
|
||||
this.rooms.set(roomToken, room);
|
||||
|
||||
eventEmitter.emit(eventName, room);
|
||||
callback(null, room);
|
||||
}, err => callback(err)).catch(err => callback(err));
|
||||
} else {
|
||||
callback(null, room);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a room.
|
||||
*
|
||||
* @param {Object} room Properties to be sent to the LoopServer
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be the list of rooms, if it was fetched successfully.
|
||||
* be the room, if it was created successfully.
|
||||
*/
|
||||
getAll: function(callback) {
|
||||
return LoopRoomsInternal.getAll(callback);
|
||||
create: function(room, callback) {
|
||||
if (!("roomName" in room) || !("expiresIn" in room) ||
|
||||
!("roomOwner" in room) || !("maxSize" in room)) {
|
||||
callback(new Error("Missing required property to create a room"));
|
||||
return;
|
||||
}
|
||||
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
|
||||
MozLoopService.hawkRequest(sessionType, "/rooms", "POST", room)
|
||||
.then(response => {
|
||||
let data = JSON.parse(response.body);
|
||||
extend(room, data);
|
||||
// Do not keep this value - it is a request to the server.
|
||||
delete room.expiresIn;
|
||||
this.rooms.set(room.roomToken, room);
|
||||
|
||||
eventEmitter.emit("add", room);
|
||||
callback(null, room);
|
||||
}, error => callback(error)).catch(error => callback(error));
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current stored version of the data for the indicated room.
|
||||
* Callback used to indicate changes to rooms data on the LoopServer.
|
||||
*
|
||||
* @param {String} localRoomId Local room identifier
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be the list of rooms, if it was fetched successfully.
|
||||
* @param {String} version Version number assigned to this change set.
|
||||
* @param {String} channelID Notification channel identifier.
|
||||
*/
|
||||
getRoomData: function(localRoomId, callback) {
|
||||
return LoopRoomsInternal.getRoomData(localRoomId, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a room. Will both open a chat window for the new room
|
||||
* and perform an exchange with the LoopServer to create the room.
|
||||
* for a callback type. Callback must be of the form:
|
||||
* function (error, success) {...}
|
||||
*
|
||||
* @param {Object} room properties to be sent to the LoopServer
|
||||
* @param {Function} callback Must be of the form: function (error, success) {...}
|
||||
*
|
||||
* @returns {String} localRoomId assigned to this new room.
|
||||
*/
|
||||
createRoom: function(roomProps, callback) {
|
||||
return LoopRoomsInternal.createRoom(roomProps, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a callback of a specified type with a localRoomId.
|
||||
*
|
||||
* @param {String} localRoomId Local room identifier.
|
||||
* @param {String} callbackName callback type
|
||||
* @param {Function} callback Must be of the form: function (error, success) {...}
|
||||
*/
|
||||
addCallback: function(localRoomId, callbackName, callback) {
|
||||
return LoopRoomsInternal.addCallback(localRoomId, callbackName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Un-register and delete a callback of a specified type for a localRoomId.
|
||||
*
|
||||
* @param {String} localRoomId Local room identifier.
|
||||
* @param {String} callbackName callback type
|
||||
* @param {Function} callback Previously passed to addCallback().
|
||||
*/
|
||||
deleteCallback: function(localRoomId, callbackName, callback) {
|
||||
return LoopRoomsInternal.deleteCallback(localRoomId, callbackName, callback);
|
||||
onNotification: function(version, channelID) {
|
||||
gDirty = true;
|
||||
this.getAll(version, () => {});
|
||||
},
|
||||
};
|
||||
Object.freeze(LoopRooms);
|
||||
Object.freeze(LoopRoomsInternal);
|
||||
|
||||
/**
|
||||
* Public Loop Rooms API.
|
||||
*
|
||||
* LoopRooms implements the EventEmitter interface by exposing three methods -
|
||||
* `on`, `once` and `off` - to subscribe to events.
|
||||
* At this point the following events may be subscribed to:
|
||||
* - 'add': A new room object was successfully added to the data store.
|
||||
* - 'remove': A room was successfully removed from the data store.
|
||||
* - 'update': A room object was successfully updated with changed
|
||||
* properties in the data store.
|
||||
*
|
||||
* See the internal code for the API documentation.
|
||||
*/
|
||||
this.LoopRooms = {
|
||||
getAll: function(version, callback) {
|
||||
return LoopRoomsInternal.getAll(version, callback);
|
||||
},
|
||||
|
||||
get: function(roomToken, callback) {
|
||||
return LoopRoomsInternal.get(roomToken, callback);
|
||||
},
|
||||
|
||||
create: function(options, callback) {
|
||||
return LoopRoomsInternal.create(options, callback);
|
||||
},
|
||||
|
||||
promise: function(method, ...params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this[method](...params, (error, result) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
on: (...params) => eventEmitter.on(...params),
|
||||
|
||||
once: (...params) => eventEmitter.once(...params),
|
||||
|
||||
off: (...params) => eventEmitter.off(...params)
|
||||
};
|
||||
Object.freeze(this.LoopRooms);
|
||||
|
@ -75,6 +75,14 @@ const cloneValueInto = function(value, targetWindow) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Strip Function properties, since they can not be cloned across boundaries
|
||||
// like this.
|
||||
for (let prop of Object.getOwnPropertyNames(value)) {
|
||||
if (typeof value[prop] == "function") {
|
||||
delete value[prop];
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect for an error this way, because the Error object is special.
|
||||
if (value.constructor.name == "Error") {
|
||||
return cloneErrorObject(value, targetWindow);
|
||||
@ -176,8 +184,10 @@ function injectLoopAPI(targetWindow) {
|
||||
}
|
||||
|
||||
// We have to clone the error property since it may be an Error object.
|
||||
if (error.hasOwnProperty("toString")) {
|
||||
delete error.toString;
|
||||
}
|
||||
errors[type] = Cu.cloneInto(error, targetWindow);
|
||||
|
||||
}
|
||||
return Cu.cloneInto(errors, targetWindow);
|
||||
},
|
||||
@ -196,34 +206,34 @@ function injectLoopAPI(targetWindow) {
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the callData for a specific callDataId
|
||||
* Returns the callData for a specific conversation window id.
|
||||
*
|
||||
* The data was retrieved from the LoopServer via a GET/calls/<version> request
|
||||
* triggered by an incoming message from the LoopPushServer.
|
||||
*
|
||||
* @param {int} loopCallId
|
||||
* @param {Number} conversationWindowId
|
||||
* @returns {callData} The callData or undefined if error.
|
||||
*/
|
||||
getCallData: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(loopCallId) {
|
||||
return Cu.cloneInto(LoopCalls.getCallData(loopCallId), targetWindow);
|
||||
value: function(conversationWindowId) {
|
||||
return Cu.cloneInto(LoopCalls.getCallData(conversationWindowId), targetWindow);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Releases the callData for a specific loopCallId
|
||||
* Releases the callData for a specific conversation window id.
|
||||
*
|
||||
* The result of this call will be a free call session slot.
|
||||
*
|
||||
* @param {int} loopCallId
|
||||
* @param {Number} conversationWindowId
|
||||
*/
|
||||
releaseCallData: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(loopCallId) {
|
||||
LoopCalls.releaseCallData(loopCallId);
|
||||
value: function(conversationWindowId) {
|
||||
LoopCalls.releaseCallData(conversationWindowId);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -352,7 +352,7 @@ loop.conversation = (function(mozL10n) {
|
||||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("callId"));
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("windowId"));
|
||||
if (!callData) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
@ -374,7 +374,7 @@ loop.conversation = (function(mozL10n) {
|
||||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("callId"));
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
@ -475,7 +475,7 @@ loop.conversation = (function(mozL10n) {
|
||||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("callId"));
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
@ -618,10 +618,10 @@ loop.conversation = (function(mozL10n) {
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
|
||||
// Obtain the callId and pass it through
|
||||
// Obtain the windowId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationData().hash;
|
||||
var callId;
|
||||
var windowId;
|
||||
var outgoing;
|
||||
var localRoomStore;
|
||||
|
||||
@ -633,7 +633,7 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
var hash = locationHash.match(/#incoming\/(.*)/);
|
||||
if (hash) {
|
||||
callId = hash[1];
|
||||
windowId = hash[1];
|
||||
outgoing = false;
|
||||
} else if (hash = locationHash.match(/#room\/(.*)/)) {
|
||||
localRoomStore = new loop.store.LocalRoomStore({
|
||||
@ -643,16 +643,16 @@ loop.conversation = (function(mozL10n) {
|
||||
} else {
|
||||
hash = locationHash.match(/#outgoing\/(.*)/);
|
||||
if (hash) {
|
||||
callId = hash[1];
|
||||
windowId = hash[1];
|
||||
outgoing = true;
|
||||
}
|
||||
}
|
||||
|
||||
conversation.set({callId: callId});
|
||||
conversation.set({windowId: windowId});
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(callId);
|
||||
navigator.mozLoop.releaseCallData(windowId);
|
||||
});
|
||||
|
||||
React.renderComponent(AppControllerView({
|
||||
@ -671,7 +671,7 @@ loop.conversation = (function(mozL10n) {
|
||||
}
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
callId: callId,
|
||||
windowId: windowId,
|
||||
outgoing: outgoing
|
||||
}));
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ loop.conversation = (function(mozL10n) {
|
||||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("callId"));
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("windowId"));
|
||||
if (!callData) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
@ -374,7 +374,7 @@ loop.conversation = (function(mozL10n) {
|
||||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("callId"));
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
@ -475,7 +475,7 @@ loop.conversation = (function(mozL10n) {
|
||||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("callId"));
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
@ -618,10 +618,10 @@ loop.conversation = (function(mozL10n) {
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
|
||||
// Obtain the callId and pass it through
|
||||
// Obtain the windowId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationData().hash;
|
||||
var callId;
|
||||
var windowId;
|
||||
var outgoing;
|
||||
var localRoomStore;
|
||||
|
||||
@ -633,7 +633,7 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
var hash = locationHash.match(/#incoming\/(.*)/);
|
||||
if (hash) {
|
||||
callId = hash[1];
|
||||
windowId = hash[1];
|
||||
outgoing = false;
|
||||
} else if (hash = locationHash.match(/#room\/(.*)/)) {
|
||||
localRoomStore = new loop.store.LocalRoomStore({
|
||||
@ -643,16 +643,16 @@ loop.conversation = (function(mozL10n) {
|
||||
} else {
|
||||
hash = locationHash.match(/#outgoing\/(.*)/);
|
||||
if (hash) {
|
||||
callId = hash[1];
|
||||
windowId = hash[1];
|
||||
outgoing = true;
|
||||
}
|
||||
}
|
||||
|
||||
conversation.set({callId: callId});
|
||||
conversation.set({windowId: windowId});
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(callId);
|
||||
navigator.mozLoop.releaseCallData(windowId);
|
||||
});
|
||||
|
||||
React.renderComponent(<AppControllerView
|
||||
@ -671,7 +671,7 @@ loop.conversation = (function(mozL10n) {
|
||||
}
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
callId: callId,
|
||||
windowId: windowId,
|
||||
outgoing: outgoing
|
||||
}));
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ loop.shared.actions = (function() {
|
||||
*/
|
||||
GatherCallData: Action.define("gatherCallData", {
|
||||
// Specify the callId for an incoming call.
|
||||
callId: [String, null],
|
||||
windowId: [String, null],
|
||||
outgoing: Boolean
|
||||
}),
|
||||
|
||||
|
@ -55,6 +55,8 @@ loop.store.ConversationStore = (function() {
|
||||
|
||||
var ConversationStore = Backbone.Model.extend({
|
||||
defaults: {
|
||||
// The id of the window. Currently used for getting the window id.
|
||||
windowId: undefined,
|
||||
// The current state of the call
|
||||
callState: CALL_STATES.INIT,
|
||||
// The reason if a call was terminated
|
||||
@ -200,7 +202,7 @@ loop.store.ConversationStore = (function() {
|
||||
return;
|
||||
}
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(actionData.callId);
|
||||
var callData = navigator.mozLoop.getCallData(actionData.windowId);
|
||||
if (!callData) {
|
||||
console.error("Failed to get the call data");
|
||||
this.set({callState: CALL_STATES.TERMINATED});
|
||||
@ -210,7 +212,7 @@ loop.store.ConversationStore = (function() {
|
||||
this.set({
|
||||
contact: callData.contact,
|
||||
outgoing: actionData.outgoing,
|
||||
callId: actionData.callId,
|
||||
windowId: actionData.windowId,
|
||||
callType: callData.callType,
|
||||
callState: CALL_STATES.GATHER
|
||||
});
|
||||
@ -407,11 +409,7 @@ loop.store.ConversationStore = (function() {
|
||||
delete this._websocket;
|
||||
}
|
||||
|
||||
// XXX: The internal callId is different from
|
||||
// this.get("callId"), see bug 1084228 for more info.
|
||||
var locationHash = new loop.shared.utils.Helper().locationData().hash;
|
||||
var callId = locationHash.match(/\#outgoing\/(.*)/)[1];
|
||||
navigator.mozLoop.releaseCallData(callId);
|
||||
navigator.mozLoop.releaseCallData(this.get("windowId"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@ loop.shared.models = (function(l10n) {
|
||||
sessionToken: undefined, // OT session token
|
||||
sessionType: undefined, // Hawk session type
|
||||
apiKey: undefined, // OT api key
|
||||
windowId: undefined, // The window id
|
||||
callId: undefined, // The callId on the server
|
||||
progressURL: undefined, // The websocket url to use for progress
|
||||
websocketToken: undefined, // The token to use for websocket auth, this is
|
||||
|
@ -158,7 +158,7 @@ describe("loop.conversation", function() {
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.GatherCallData({
|
||||
callId: "42",
|
||||
windowId: "42",
|
||||
outgoing: false
|
||||
}));
|
||||
});
|
||||
@ -175,7 +175,7 @@ describe("loop.conversation", function() {
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.GatherCallData({
|
||||
callId: "24",
|
||||
windowId: "24",
|
||||
outgoing: true
|
||||
}));
|
||||
});
|
||||
@ -276,7 +276,7 @@ describe("loop.conversation", function() {
|
||||
conversation = new loop.shared.models.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
conversation.set({callId: 42});
|
||||
conversation.set({windowId: 42});
|
||||
sandbox.stub(conversation, "setOutgoingSessionData");
|
||||
});
|
||||
|
||||
@ -547,8 +547,10 @@ describe("loop.conversation", function() {
|
||||
decline: sinon.stub(),
|
||||
close: sinon.stub()
|
||||
};
|
||||
conversation.set({
|
||||
windowId: "8699"
|
||||
});
|
||||
conversation.setIncomingSessionData({
|
||||
callId: 8699,
|
||||
websocketToken: 123
|
||||
});
|
||||
});
|
||||
@ -571,7 +573,7 @@ describe("loop.conversation", function() {
|
||||
icView.decline();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, 8699);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "8699");
|
||||
});
|
||||
});
|
||||
|
||||
@ -610,7 +612,7 @@ describe("loop.conversation", function() {
|
||||
|
||||
sinon.assert.calledTwice(conversation.get);
|
||||
sinon.assert.calledWithExactly(conversation.get, "callToken");
|
||||
sinon.assert.calledWithExactly(conversation.get, "callId");
|
||||
sinon.assert.calledWithExactly(conversation.get, "windowId");
|
||||
});
|
||||
|
||||
it("should trigger error handling in case of error", function() {
|
||||
|
@ -125,11 +125,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
describe("#connectionFailure", function() {
|
||||
beforeEach(function() {
|
||||
store._websocket = fakeWebsocket;
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationData")
|
||||
.returns({
|
||||
hash: "#outgoing/42",
|
||||
pathname: ""
|
||||
});
|
||||
store.set({windowId: "42"});
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
@ -246,7 +242,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
it("should set the state to 'gather'", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
callId: "76543218",
|
||||
windowId: "76543218",
|
||||
outgoing: true
|
||||
}));
|
||||
|
||||
@ -256,18 +252,18 @@ describe("loop.store.ConversationStore", function () {
|
||||
it("should save the basic call information", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
callId: "123456",
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
}));
|
||||
|
||||
expect(store.get("callId")).eql("123456");
|
||||
expect(store.get("windowId")).eql("123456");
|
||||
expect(store.get("outgoing")).eql(true);
|
||||
});
|
||||
|
||||
it("should save the basic information from the mozLoop api", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
callId: "123456",
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
}));
|
||||
|
||||
@ -280,7 +276,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
|
||||
beforeEach(function() {
|
||||
outgoingCallData = {
|
||||
callId: "123456",
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
};
|
||||
});
|
||||
@ -499,11 +495,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
close: wsCloseSpy
|
||||
};
|
||||
store.set({callState: CALL_STATES.ONGOING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationData")
|
||||
.returns({
|
||||
hash: "#outgoing/42",
|
||||
pathname: ""
|
||||
});
|
||||
store.set({windowId: "42"});
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
@ -549,11 +541,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
close: wsCloseSpy
|
||||
};
|
||||
store.set({callState: CALL_STATES.ONGOING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationData")
|
||||
.returns({
|
||||
hash: "#outgoing/42",
|
||||
pathname: ""
|
||||
});
|
||||
store.set({windowId: "42"});
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
@ -587,11 +575,7 @@ describe("loop.store.ConversationStore", function () {
|
||||
store._websocket = fakeWebsocket;
|
||||
|
||||
store.set({callState: CALL_STATES.CONNECTING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationData")
|
||||
.returns({
|
||||
hash: "#outgoing/42",
|
||||
pathname: ""
|
||||
});
|
||||
store.set({windowId: "42"});
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
|
@ -45,7 +45,7 @@ describe("loop.Dispatcher", function () {
|
||||
|
||||
beforeEach(function() {
|
||||
gatherAction = new sharedActions.GatherCallData({
|
||||
callId: "42",
|
||||
windowId: "42",
|
||||
outgoing: false
|
||||
});
|
||||
|
||||
|
175
browser/components/loop/test/xpcshell/test_looprooms.js
Normal file
@ -0,0 +1,175 @@
|
||||
/* 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/. */
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||
|
||||
const kRooms = new Map([
|
||||
["_nxD4V4FflQ", {
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517546
|
||||
}],
|
||||
["QzBbvGmIZWU", {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/QzBbvGmIZWU",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 140551741
|
||||
}],
|
||||
["3jKS_Els9IU", {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/3jKS_Els9IU",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
currSize: 1,
|
||||
ctime: 1405518241
|
||||
}]
|
||||
]);
|
||||
|
||||
let roomDetail = {
|
||||
roomName: "First Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
roomOwner: "Alexis",
|
||||
maxSize: 2,
|
||||
clientMaxSize: 2,
|
||||
creationTime: 1405517546,
|
||||
expiresAt: 1405534180,
|
||||
participants: [{
|
||||
displayName: "Alexis",
|
||||
account: "alexis@example.com",
|
||||
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
|
||||
}, {
|
||||
displayName: "Adam",
|
||||
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
|
||||
}]
|
||||
};
|
||||
|
||||
const kCreateRoomProps = {
|
||||
roomName: "UX Discussion",
|
||||
expiresIn: 5,
|
||||
roomOwner: "Alexis",
|
||||
maxSize: 2
|
||||
};
|
||||
|
||||
const kCreateRoomData = {
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
expiresAt: 1405534180
|
||||
};
|
||||
|
||||
add_task(function* setup_server() {
|
||||
loopServer.registerPathHandler("/registration", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
|
||||
if (req.method == "POST") {
|
||||
Assert.ok(req.bodyInputStream, "POST request should have a payload");
|
||||
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
Assert.deepEqual(data, kCreateRoomProps);
|
||||
|
||||
res.write(JSON.stringify(kCreateRoomData));
|
||||
} else {
|
||||
res.write(JSON.stringify([...kRooms.values()]));
|
||||
}
|
||||
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
function returnRoomDetails(res, roomName) {
|
||||
roomDetail.roomName = roomName;
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(roomDetail));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
}
|
||||
|
||||
// Add a request handler for each room in the list.
|
||||
[...kRooms.values()].forEach(function(room) {
|
||||
loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
|
||||
returnRoomDetails(res, room.roomName);
|
||||
});
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/error401", (req, res) => {
|
||||
res.setStatusLine(null, 401, "Not Found");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/errorMalformed", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write("{\"some\": \"Syntax Error!\"}}}}}}");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
});
|
||||
|
||||
const normalizeRoom = function(room) {
|
||||
delete room.currSize;
|
||||
if (!("participants" in room)) {
|
||||
let name = room.roomName;
|
||||
for (let key of Object.getOwnPropertyNames(roomDetail)) {
|
||||
room[key] = roomDetail[key];
|
||||
}
|
||||
room.roomName = name;
|
||||
}
|
||||
return room;
|
||||
};
|
||||
|
||||
const compareRooms = function(room1, room2) {
|
||||
Assert.deepEqual(normalizeRoom(room1), normalizeRoom(room2));
|
||||
};
|
||||
|
||||
add_task(function* test_getAllRooms() {
|
||||
yield MozLoopService.register(mockPushHandler);
|
||||
|
||||
let rooms = yield LoopRooms.promise("getAll");
|
||||
Assert.equal(rooms.length, 3);
|
||||
for (let room of rooms) {
|
||||
compareRooms(kRooms.get(room.roomToken), room);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_getRoom() {
|
||||
yield MozLoopService.register(mockPushHandler);
|
||||
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let room = yield LoopRooms.promise("get", roomToken);
|
||||
Assert.deepEqual(room, kRooms.get(roomToken));
|
||||
});
|
||||
|
||||
add_task(function* test_errorStates() {
|
||||
yield Assert.rejects(LoopRooms.promise("get", "error401"), /Not Found/, "Fetching a non-existent room should fail");
|
||||
yield Assert.rejects(LoopRooms.promise("get", "errorMalformed"), /SyntaxError/, "Wrong message format should reject");
|
||||
});
|
||||
|
||||
add_task(function* test_createRoom() {
|
||||
let eventCalled = false;
|
||||
LoopRooms.once("add", (e, room) => {
|
||||
compareRooms(room, kCreateRoomProps);
|
||||
eventCalled = true;
|
||||
});
|
||||
let room = yield LoopRooms.promise("create", kCreateRoomProps);
|
||||
compareRooms(room, kCreateRoomProps);
|
||||
Assert.ok(eventCalled, "Event should have fired");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
|
||||
"resource:///modules/Chat.jsm");
|
||||
let hasTheseProps = function(a, b) {
|
||||
for (let prop in a) {
|
||||
if (a[prop] != b[prop]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let openChatOrig = Chat.open;
|
||||
|
||||
add_test(function test_openRoomsWindow() {
|
||||
let roomProps = {roomName: "UX Discussion",
|
||||
expiresIn: 5,
|
||||
roomOwner: "Alexis",
|
||||
maxSize: 2}
|
||||
|
||||
let roomData = {roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
expiresAt: 1405534180}
|
||||
|
||||
loopServer.registerPathHandler("/rooms", (request, response) => {
|
||||
if (!request.bodyInputStream) {
|
||||
do_throw("empty request body");
|
||||
}
|
||||
let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
do_check_true(hasTheseProps(roomProps, data));
|
||||
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify(roomData));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
});
|
||||
|
||||
MozLoopService.register(mockPushHandler).then(() => {
|
||||
let opened = false;
|
||||
let created = false;
|
||||
let urlPieces = [];
|
||||
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
urlPieces = url.split('/');
|
||||
do_check_eq(urlPieces[0], "about:loopconversation#room");
|
||||
opened = true;
|
||||
};
|
||||
|
||||
let returnedID = LoopRooms.createRoom(roomProps, (error, data) => {
|
||||
do_check_false(error);
|
||||
do_check_true(data);
|
||||
do_check_true(hasTheseProps(roomData, data));
|
||||
do_check_eq(data.localRoomId, urlPieces[1]);
|
||||
created = true;
|
||||
});
|
||||
|
||||
waitForCondition(function() created && opened).then(() => {
|
||||
do_check_true(opened, "should open a chat window");
|
||||
do_check_eq(returnedID, urlPieces[1]);
|
||||
|
||||
// Verify that a delayed callback, when attached,
|
||||
// received the same data.
|
||||
LoopRooms.addCallback(
|
||||
urlPieces[1], "RoomCreated",
|
||||
(error, data) => {
|
||||
do_check_false(error);
|
||||
do_check_true(data);
|
||||
do_check_true(hasTheseProps(roomData, data));
|
||||
do_check_eq(data.localRoomId, urlPieces[1]);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setupFakeLoopServer();
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
});
|
||||
|
||||
do_register_cleanup(function() {
|
||||
// Revert original Chat.open implementation
|
||||
Chat.open = openChatOrig;
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
|
||||
"resource:///modules/Chat.jsm");
|
||||
let hasTheseProps = function(a, b) {
|
||||
for (let prop in a) {
|
||||
if (a[prop] != b[prop]) {
|
||||
do_print("hasTheseProps fail: prop = " + prop);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let openChatOrig = Chat.open;
|
||||
|
||||
add_test(function test_getAllRooms() {
|
||||
|
||||
let roomList = [
|
||||
{ roomToken: "_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517546 },
|
||||
{ roomToken: "QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/QzBbvGmIZWU",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 140551741 },
|
||||
{ roomToken: "3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/3jKS_Els9IU",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
currSize: 1,
|
||||
ctime: 1405518241 }
|
||||
]
|
||||
|
||||
let roomDetail = {
|
||||
roomName: "First Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
roomOwner: "Alexis",
|
||||
maxSize: 2,
|
||||
clientMaxSize: 2,
|
||||
creationTime: 1405517546,
|
||||
expiresAt: 1405534180,
|
||||
participants: [
|
||||
{ displayName: "Alexis", account: "alexis@example.com", roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
|
||||
{ displayName: "Adam", roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
|
||||
]
|
||||
}
|
||||
|
||||
loopServer.registerPathHandler("/rooms", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify(roomList));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
});
|
||||
|
||||
let returnRoomDetails = function(response, roomName) {
|
||||
roomDetail.roomName = roomName;
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify(roomDetail));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
}
|
||||
|
||||
loopServer.registerPathHandler("/rooms/_nxD4V4FflQ", (request, response) => {
|
||||
returnRoomDetails(response, "First Room Name");
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/QzBbvGmIZWU", (request, response) => {
|
||||
returnRoomDetails(response, "Second Room Name");
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/3jKS_Els9IU", (request, response) => {
|
||||
returnRoomDetails(response, "Third Room Name");
|
||||
});
|
||||
|
||||
MozLoopService.register().then(() => {
|
||||
|
||||
LoopRooms.getAll((error, rooms) => {
|
||||
do_check_false(error);
|
||||
do_check_true(rooms);
|
||||
do_check_eq(rooms.length, 3);
|
||||
do_check_eq(rooms[0].roomName, "First Room Name");
|
||||
do_check_eq(rooms[1].roomName, "Second Room Name");
|
||||
do_check_eq(rooms[2].roomName, "Third Room Name");
|
||||
|
||||
let room = rooms[0];
|
||||
do_check_true(room.localRoomId);
|
||||
do_check_false(room.currSize);
|
||||
delete roomList[0].currSize;
|
||||
do_check_true(hasTheseProps(roomList[0], room));
|
||||
delete roomDetail.roomName;
|
||||
delete room.participants;
|
||||
delete roomDetail.participants;
|
||||
do_check_true(hasTheseProps(roomDetail, room));
|
||||
|
||||
LoopRooms.getRoomData(room.localRoomId, (error, roomData) => {
|
||||
do_check_false(error);
|
||||
do_check_true(hasTheseProps(room, roomData));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
});
|
||||
|
||||
do_register_cleanup(function() {
|
||||
// Revert original Chat.open implementation
|
||||
Chat.open = openChatOrig;
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -6,6 +6,7 @@ skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_loopapi_hawk_request.js]
|
||||
[test_looppush_initialize.js]
|
||||
[test_looprooms.js]
|
||||
[test_loopservice_directcall.js]
|
||||
[test_loopservice_dnd.js]
|
||||
[test_loopservice_expiry.js]
|
||||
@ -21,5 +22,3 @@ skip-if = toolkit == 'gonk'
|
||||
[test_loopservice_token_send.js]
|
||||
[test_loopservice_token_validation.js]
|
||||
[test_loopservice_busy.js]
|
||||
[test_rooms_getdata.js]
|
||||
[test_rooms_create.js]
|
||||
|
@ -401,6 +401,7 @@ BrowserGlue.prototype = {
|
||||
Services.obs.removeObserver(this, "browser-search-service");
|
||||
this._syncSearchEngines();
|
||||
break;
|
||||
#ifdef NIGHTLY_BUILD
|
||||
case "nsPref:changed":
|
||||
if (data == POLARIS_ENABLED) {
|
||||
let enabled = Services.prefs.getBoolPref(POLARIS_ENABLED);
|
||||
@ -408,6 +409,7 @@ BrowserGlue.prototype = {
|
||||
Services.prefs.setBoolPref("privacy.trackingprotection.enabled", enabled);
|
||||
Services.prefs.setBoolPref("privacy.trackingprotection.ui.enabled", enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -14,6 +14,24 @@ var gPrivacyPane = {
|
||||
*/
|
||||
_shouldPromptForRestart: true,
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
/**
|
||||
* Show the Tracking Protection UI depending on the
|
||||
* privacy.trackingprotection.ui.enabled pref, and linkify its Learn More link
|
||||
*/
|
||||
_initTrackingProtection: function () {
|
||||
if (!Services.prefs.getBoolPref("privacy.trackingprotection.ui.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let link = document.getElementById("trackingProtectionLearnMore");
|
||||
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
|
||||
link.setAttribute("href", url);
|
||||
|
||||
document.getElementById("trackingprotectionbox").hidden = false;
|
||||
},
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets up the UI for the number of days of history to keep, and updates the
|
||||
* label of the "Clear Now..." button.
|
||||
@ -31,6 +49,9 @@ var gPrivacyPane = {
|
||||
this.updateHistoryModePane();
|
||||
this.updatePrivacyMicroControls();
|
||||
this.initAutoStartPrivateBrowsingReverter();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
this._initTrackingProtection();
|
||||
#endif
|
||||
|
||||
setEventListener("browser.urlbar.default.behavior", "change",
|
||||
document.getElementById('browser.urlbar.autocomplete.enabled')
|
||||
|
@ -13,6 +13,9 @@
|
||||
<preference id="privacy.donottrackheader.enabled"
|
||||
name="privacy.donottrackheader.enabled"
|
||||
type="bool"/>
|
||||
<preference id="privacy.trackingprotection.enabled"
|
||||
name="privacy.trackingprotection.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- XXX button prefs -->
|
||||
<preference id="pref.privacy.disable_button.cookie_exceptions"
|
||||
@ -71,6 +74,19 @@
|
||||
<!-- Tracking -->
|
||||
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start">
|
||||
<caption><label>&tracking.label;</label></caption>
|
||||
<vbox id="trackingprotectionbox" hidden="true">
|
||||
<hbox align="center">
|
||||
<checkbox id="trackingProtection"
|
||||
preference="privacy.trackingprotection.enabled"
|
||||
accesskey="&trackingProtection.accesskey;"
|
||||
label="&trackingProtection.label;" />
|
||||
<image id="trackingProtectionImage" src="chrome://browser/skin/bad-content-blocked-16.png"/>
|
||||
</hbox>
|
||||
<label id="trackingProtectionLearnMore"
|
||||
class="text-link"
|
||||
value="&trackingProtectionLearnMore.label;"/>
|
||||
<separator/>
|
||||
</vbox>
|
||||
<checkbox id="privacyDoNotTrackCheckbox"
|
||||
label="&dntTrackingNotOkay.label2;"
|
||||
accesskey="&dntTrackingNotOkay.accesskey;"
|
||||
|
@ -17,6 +17,24 @@ var gPrivacyPane = {
|
||||
*/
|
||||
_shouldPromptForRestart: true,
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
/**
|
||||
* Show the Tracking Protection UI depending on the
|
||||
* privacy.trackingprotection.ui.enabled pref, and linkify its Learn More link
|
||||
*/
|
||||
_initTrackingProtection: function () {
|
||||
if (!Services.prefs.getBoolPref("privacy.trackingprotection.ui.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let link = document.getElementById("trackingProtectionLearnMore");
|
||||
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
|
||||
link.setAttribute("href", url);
|
||||
|
||||
document.getElementById("trackingprotectionbox").hidden = false;
|
||||
},
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets up the UI for the number of days of history to keep, and updates the
|
||||
* label of the "Clear Now..." button.
|
||||
@ -28,6 +46,9 @@ var gPrivacyPane = {
|
||||
this.updateHistoryModePane();
|
||||
this.updatePrivacyMicroControls();
|
||||
this.initAutoStartPrivateBrowsingReverter();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
this._initTrackingProtection();
|
||||
#endif
|
||||
},
|
||||
|
||||
// HISTORY MODE
|
||||
|
@ -26,6 +26,9 @@
|
||||
<preference id="privacy.donottrackheader.enabled"
|
||||
name="privacy.donottrackheader.enabled"
|
||||
type="bool"/>
|
||||
<preference id="privacy.trackingprotection.enabled"
|
||||
name="privacy.trackingprotection.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- XXX button prefs -->
|
||||
<preference id="pref.privacy.disable_button.cookie_exceptions"
|
||||
@ -81,6 +84,19 @@
|
||||
<!-- Tracking -->
|
||||
<groupbox id="trackingGroup" align="start">
|
||||
<caption label="&tracking.label;"/>
|
||||
<vbox id="trackingprotectionbox" hidden="true">
|
||||
<hbox align="center">
|
||||
<checkbox id="trackingProtection"
|
||||
preference="privacy.trackingprotection.enabled"
|
||||
accesskey="&trackingProtection.accesskey;"
|
||||
label="&trackingProtection.label;" />
|
||||
<image id="trackingProtectionImage" src="chrome://browser/skin/bad-content-blocked-16.png"/>
|
||||
</hbox>
|
||||
<label id="trackingProtectionLearnMore"
|
||||
class="text-link"
|
||||
value="&trackingProtectionLearnMore.label;"/>
|
||||
<separator/>
|
||||
</vbox>
|
||||
<checkbox id="privacyDoNotTrackCheckbox"
|
||||
label="&dntTrackingNotOkay.label2;"
|
||||
accesskey="&dntTrackingNotOkay.accesskey;"
|
||||
|
@ -23,12 +23,24 @@ function* testPrefs(test) {
|
||||
}
|
||||
}
|
||||
|
||||
function isNightly() {
|
||||
return Services.appinfo.version.contains("a1");
|
||||
}
|
||||
|
||||
add_task(function* test_default_values() {
|
||||
if (!isNightly()) {
|
||||
ok(true, "Skipping test, not Nightly")
|
||||
return;
|
||||
}
|
||||
Assert.ok(!Services.prefs.getBoolPref(POLARIS_ENABLED), POLARIS_ENABLED + " is disabled by default.");
|
||||
Assert.ok(!Services.prefs.getBoolPref(PREF_TPUI), PREF_TPUI + "is disabled by default.");
|
||||
});
|
||||
|
||||
add_task(function* test_changing_pref_changes_tracking() {
|
||||
if (!isNightly()) {
|
||||
ok(true, "Skipping test, not Nightly")
|
||||
return;
|
||||
}
|
||||
function* testPref(pref) {
|
||||
Services.prefs.setBoolPref(POLARIS_ENABLED, true);
|
||||
yield assertPref(pref, true);
|
||||
@ -41,6 +53,10 @@ add_task(function* test_changing_pref_changes_tracking() {
|
||||
});
|
||||
|
||||
add_task(function* test_prefs_can_be_changed_individually() {
|
||||
if (!isNightly()) {
|
||||
ok(true, "Skipping test, not Nightly")
|
||||
return;
|
||||
}
|
||||
function* testPref(pref) {
|
||||
Services.prefs.setBoolPref(POLARIS_ENABLED, true);
|
||||
yield assertPref(pref, true);
|
||||
|
@ -44,8 +44,14 @@ const TIMELINE_BLUEPRINT = {
|
||||
stroke: "hsl(39,82%,49%)",
|
||||
label: L10N.getStr("timeline.label.paint")
|
||||
},
|
||||
"ConsoleTime": {
|
||||
"DOMEvent": {
|
||||
group: 3,
|
||||
fill: "hsl(219,82%,69%)",
|
||||
stroke: "hsl(219,82%,69%)",
|
||||
label: L10N.getStr("timeline.label.domevent")
|
||||
},
|
||||
"ConsoleTime": {
|
||||
group: 4,
|
||||
fill: "hsl(0,0%,80%)",
|
||||
stroke: "hsl(0,0%,60%)",
|
||||
label: L10N.getStr("timeline.label.consoleTime")
|
||||
|
@ -231,6 +231,7 @@
|
||||
@BINPATH@/components/dom_stylesheets.xpt
|
||||
@BINPATH@/components/dom_telephony.xpt
|
||||
@BINPATH@/components/dom_traversal.xpt
|
||||
@BINPATH@/components/dom_tv.xpt
|
||||
@BINPATH@/components/dom_voicemail.xpt
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
@BINPATH@/components/dom_webspeechrecognition.xpt
|
||||
|
@ -17,6 +17,12 @@ setDefaultBrowserNotNow.accesskey = N
|
||||
setDefaultBrowserNever.label = Don't ask me again
|
||||
setDefaultBrowserNever.accesskey = D
|
||||
|
||||
# LOCALIZATION NOTE (setDefaultBrowserTitle, setDefaultBrowserMessage, setDefaultBrowserDontAsk):
|
||||
# These strings are used as an alternative to the ones above, in a modal dialog.
|
||||
# %S will be replaced by brandShortName
|
||||
setDefaultBrowserTitle=Default Browser
|
||||
setDefaultBrowserMessage=%S is not currently set as your default browser. Would you like to make it your default browser?
|
||||
setDefaultBrowserDontAsk=Always perform this check when starting %S.
|
||||
|
||||
desktopBackgroundLeafNameWin=Desktop Background.bmp
|
||||
DesktopBackgroundDownloading=Saving Picture…
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 445 B |
@ -32,7 +32,11 @@ toolbar[brighttext] #downloads-button[cui-areatype="toolbar"]:not([attention]) >
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="toolbar"][attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button[cui-areatype="toolbar"][attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"][attention] {
|
||||
@ -54,7 +58,11 @@ toolbar[brighttext] #downloads-button:not([counter]):not([attention]) > #downloa
|
||||
}
|
||||
|
||||
#downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
/*** Download notifications ***/
|
||||
|
@ -108,7 +108,6 @@ browser.jar:
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
|
||||
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 676 B |
Before Width: | Height: | Size: 1.2 KiB |
@ -34,7 +34,11 @@ toolbar[brighttext] #downloads-indicator-icon {
|
||||
}
|
||||
|
||||
#downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"][attention] {
|
||||
@ -56,7 +60,11 @@ toolbar[brighttext] #downloads-button:not([counter]):not([attention]) > #downloa
|
||||
}
|
||||
|
||||
#downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
@ -79,7 +87,11 @@ toolbar[brighttext] #downloads-button:not([counter]):not([attention]) > #downloa
|
||||
}
|
||||
|
||||
#downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"][attention] {
|
||||
@ -87,7 +99,11 @@ toolbar[brighttext] #downloads-button:not([counter]):not([attention]) > #downloa
|
||||
}
|
||||
|
||||
#downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,8 +182,6 @@ browser.jar:
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/buttons@2x.png (downloads/buttons@2x.png)
|
||||
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/browser/downloads/download-glow@2x.png (downloads/download-glow@2x.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
|
||||
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.5 KiB |
@ -32,7 +32,11 @@ toolbar[brighttext] #downloads-button:not([attention]) > #downloads-indicator-an
|
||||
}
|
||||
|
||||
#downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"][attention] {
|
||||
@ -44,10 +48,6 @@ toolbar[brighttext] #downloads-button:not([attention]) > #downloads-indicator-an
|
||||
@media (-moz-os-version: windows-vista),
|
||||
(-moz-os-version: windows-win7) {
|
||||
%endif
|
||||
#downloads-button[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow-XPVista7.png");
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"][attention] {
|
||||
list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel-XPVista7.png");
|
||||
}
|
||||
@ -69,7 +69,11 @@ toolbar[brighttext] #downloads-button:not([counter]):not([attention]) > #downloa
|
||||
}
|
||||
|
||||
#downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-button:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
|
||||
}
|
||||
|
||||
/*** Download notifications ***/
|
||||
|
@ -132,7 +132,6 @@ browser.jar:
|
||||
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/download-glow-XPVista7.png (downloads/download-glow-XPVista7.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png (downloads/download-glow-menuPanel-XPVista7.png)
|
||||
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
|
||||
@ -564,8 +563,6 @@ browser.jar:
|
||||
* skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css)
|
||||
skin/classic/aero/browser/downloads/buttons.png (downloads/buttons-aero.png)
|
||||
skin/classic/aero/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/aero/browser/downloads/download-glow.png (downloads/download-glow.png)
|
||||
skin/classic/aero/browser/downloads/download-glow-XPVista7.png (downloads/download-glow-XPVista7.png)
|
||||
skin/classic/aero/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
|
||||
skin/classic/aero/browser/downloads/download-glow-menuPanel-XPVista7.png (downloads/download-glow-menuPanel-XPVista7.png)
|
||||
skin/classic/aero/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
|
@ -2885,6 +2885,11 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
|
||||
if (startPayload->GetMetaData() == TRACING_INTERVAL_START) {
|
||||
bool hasSeenEnd = false;
|
||||
|
||||
// DOM events can be nested, so we must take care when searching
|
||||
// for the matching end. It doesn't hurt to apply this logic to
|
||||
// all event types.
|
||||
uint32_t markerDepth = 0;
|
||||
|
||||
// The assumption is that the devtools timeline flushes markers frequently
|
||||
// enough for the amount of markers to always be small enough that the
|
||||
// nested for loop isn't going to be a performance problem.
|
||||
@ -2898,24 +2903,32 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
|
||||
hasSeenPaintedLayer = true;
|
||||
}
|
||||
|
||||
bool isSameMarkerType = strcmp(startMarkerName, endMarkerName) == 0;
|
||||
if (strcmp(startMarkerName, endMarkerName) != 0) {
|
||||
continue;
|
||||
}
|
||||
bool isPaint = strcmp(startMarkerName, "Paint") == 0;
|
||||
|
||||
// Pair start and end markers.
|
||||
if (endPayload->GetMetaData() == TRACING_INTERVAL_END && isSameMarkerType) {
|
||||
// But ignore paint start/end if no layer has been painted.
|
||||
if (!isPaint || (isPaint && hasSeenPaintedLayer)) {
|
||||
mozilla::dom::ProfileTimelineMarker marker;
|
||||
marker.mName = NS_ConvertUTF8toUTF16(startMarkerName);
|
||||
marker.mStart = mProfileTimelineMarkers[i]->mTime;
|
||||
marker.mEnd = mProfileTimelineMarkers[j]->mTime;
|
||||
profileTimelineMarkers.AppendElement(marker);
|
||||
if (endPayload->GetMetaData() == TRACING_INTERVAL_START) {
|
||||
++markerDepth;
|
||||
} else if (endPayload->GetMetaData() == TRACING_INTERVAL_END) {
|
||||
if (markerDepth > 0) {
|
||||
--markerDepth;
|
||||
} else {
|
||||
// But ignore paint start/end if no layer has been painted.
|
||||
if (!isPaint || (isPaint && hasSeenPaintedLayer)) {
|
||||
mozilla::dom::ProfileTimelineMarker marker;
|
||||
marker.mName = NS_ConvertUTF8toUTF16(startMarkerName);
|
||||
marker.mStart = mProfileTimelineMarkers[i]->mTime;
|
||||
marker.mEnd = mProfileTimelineMarkers[j]->mTime;
|
||||
profileTimelineMarkers.AppendElement(marker);
|
||||
}
|
||||
|
||||
// We want the start to be dropped either way.
|
||||
hasSeenEnd = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// We want the start to be dropped either way.
|
||||
hasSeenEnd = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ support-files =
|
||||
file_bug1046022.html
|
||||
print_postdata.sjs
|
||||
test-form_sjis.html
|
||||
timelineMarkers-04.html
|
||||
|
||||
[browser_bug134911.js]
|
||||
skip-if = e10s # Bug ?????? - BrowserSetForcedCharacterSet() in browser.js references docShell
|
||||
@ -98,3 +99,7 @@ skip-if = e10s
|
||||
[browser_timelineMarkers-01.js]
|
||||
[browser_timelineMarkers-02.js]
|
||||
skip-if = e10s
|
||||
[browser_timelineMarkers-03.js]
|
||||
skip-if = e10s
|
||||
[browser_timelineMarkers-04.js]
|
||||
skip-if = e10s
|
||||
|
127
docshell/test/browser/browser_timelineMarkers-03.js
Normal file
@ -0,0 +1,127 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the docShell profile timeline API returns the right
|
||||
// markers for DOM events.
|
||||
|
||||
let TESTS = [{
|
||||
desc: "Event dispatch with single handler",
|
||||
setup: function() {
|
||||
content.document.body.addEventListener("dog",
|
||||
function(e) { console.log("hi"); },
|
||||
true);
|
||||
content.document.body.dispatchEvent(new Event("dog"));
|
||||
},
|
||||
check: function(markers) {
|
||||
is(markers.length, 1, "Got 1 marker");
|
||||
}
|
||||
}, {
|
||||
desc: "Event dispatch with a second handler",
|
||||
setup: function() {
|
||||
content.document.body.addEventListener("dog",
|
||||
function(e) { console.log("hi"); },
|
||||
false);
|
||||
content.document.body.dispatchEvent(new Event("dog"));
|
||||
},
|
||||
check: function(markers) {
|
||||
is(markers.length, 2, "Got 2 markers");
|
||||
}
|
||||
}, {
|
||||
desc: "Event dispatch on a new document",
|
||||
setup: function() {
|
||||
let doc = content.document.implementation.createHTMLDocument("doc");
|
||||
let p = doc.createElement("p");
|
||||
p.innerHTML = "inside";
|
||||
doc.body.appendChild(p);
|
||||
|
||||
p.addEventListener("zebra", function(e) {console.log("hi");});
|
||||
p.dispatchEvent(new Event("zebra"));
|
||||
},
|
||||
check: function(markers) {
|
||||
is(markers.length, 1, "Got 1 marker");
|
||||
}
|
||||
}, {
|
||||
desc: "Event dispatch on window",
|
||||
setup: function() {
|
||||
let doc = content.window.addEventListener("aardvark", function(e) {
|
||||
console.log("I like ants!");
|
||||
});
|
||||
|
||||
content.window.dispatchEvent(new Event("aardvark"));
|
||||
},
|
||||
check: function(markers) {
|
||||
is(markers.length, 1, "Got 1 marker");
|
||||
}
|
||||
}];
|
||||
|
||||
let test = Task.async(function*() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
yield openUrl("data:text/html;charset=utf-8,Test page");
|
||||
|
||||
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
info("Start recording");
|
||||
docShell.recordProfileTimelineMarkers = true;
|
||||
|
||||
for (let {desc, setup, check} of TESTS) {
|
||||
|
||||
info("Running test: " + desc);
|
||||
|
||||
info("Flushing the previous markers if any");
|
||||
docShell.popProfileTimelineMarkers();
|
||||
|
||||
info("Running the test setup function");
|
||||
let onMarkers = waitForMarkers(docShell);
|
||||
setup();
|
||||
info("Waiting for new markers on the docShell");
|
||||
let markers = yield onMarkers;
|
||||
|
||||
info("Running the test check function");
|
||||
check(markers.filter(m => m.name == "DOMEvent"));
|
||||
}
|
||||
|
||||
info("Stop recording");
|
||||
docShell.recordProfileTimelineMarkers = false;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
||||
function openUrl(url) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.focus();
|
||||
|
||||
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
|
||||
let linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
linkedBrowser.addEventListener("load", function onload() {
|
||||
linkedBrowser.removeEventListener("load", onload, true);
|
||||
resolve(tab);
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForMarkers(docshell) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let waitIterationCount = 0;
|
||||
let maxWaitIterationCount = 10; // Wait for 2sec maximum
|
||||
|
||||
let interval = setInterval(() => {
|
||||
let markers = docshell.popProfileTimelineMarkers();
|
||||
if (markers.length > 0) {
|
||||
clearInterval(interval);
|
||||
resolve(markers);
|
||||
}
|
||||
if (waitIterationCount > maxWaitIterationCount) {
|
||||
clearInterval(interval);
|
||||
resolve([]);
|
||||
}
|
||||
waitIterationCount++;
|
||||
}, 200);
|
||||
});
|
||||
}
|
94
docshell/test/browser/browser_timelineMarkers-04.js
Normal file
@ -0,0 +1,94 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the docShell profile timeline API returns the right
|
||||
// markers for XMLHttpRequest events.
|
||||
|
||||
let TESTS = [{
|
||||
desc: "Event dispatch from XMLHttpRequest",
|
||||
setup: function() {
|
||||
content.dispatchEvent(new Event("dog"));
|
||||
},
|
||||
check: function(markers) {
|
||||
// One subtlety here is that we have five events: the event we
|
||||
// inject in "setup", plus the four state transition events. The
|
||||
// first state transition is reported synchronously and so should
|
||||
// show up as a nested marker.
|
||||
is(markers.length, 5, "Got 5 markers");
|
||||
}
|
||||
}];
|
||||
|
||||
let test = Task.async(function*() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
const testDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
const testName = "timelineMarkers-04.html";
|
||||
|
||||
yield openUrl(testDir + testName);
|
||||
|
||||
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
info("Start recording");
|
||||
docShell.recordProfileTimelineMarkers = true;
|
||||
|
||||
for (let {desc, setup, check} of TESTS) {
|
||||
|
||||
info("Running test: " + desc);
|
||||
|
||||
info("Flushing the previous markers if any");
|
||||
docShell.popProfileTimelineMarkers();
|
||||
|
||||
info("Running the test setup function");
|
||||
let onMarkers = waitForMarkers(docShell);
|
||||
setup();
|
||||
info("Waiting for new markers on the docShell");
|
||||
let markers = yield onMarkers;
|
||||
|
||||
info("Running the test check function");
|
||||
check(markers.filter(m => m.name == "DOMEvent"));
|
||||
}
|
||||
|
||||
info("Stop recording");
|
||||
docShell.recordProfileTimelineMarkers = false;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
||||
function openUrl(url) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.focus();
|
||||
|
||||
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
|
||||
let linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
linkedBrowser.addEventListener("load", function onload() {
|
||||
linkedBrowser.removeEventListener("load", onload, true);
|
||||
resolve(tab);
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForMarkers(docshell) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let waitIterationCount = 0;
|
||||
let maxWaitIterationCount = 10; // Wait for 2sec maximum
|
||||
|
||||
let interval = setInterval(() => {
|
||||
let markers = docshell.popProfileTimelineMarkers();
|
||||
if (markers.length > 0) {
|
||||
clearInterval(interval);
|
||||
resolve(markers);
|
||||
}
|
||||
if (waitIterationCount > maxWaitIterationCount) {
|
||||
clearInterval(interval);
|
||||
resolve([]);
|
||||
}
|
||||
waitIterationCount++;
|
||||
}, 200);
|
||||
});
|
||||
}
|
28
docshell/test/browser/timelineMarkers-04.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"></meta>
|
||||
<title>markers test</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Test page</p>
|
||||
|
||||
<script>
|
||||
function do_xhr() {
|
||||
const theURL = "timelineMarkers-04.html";
|
||||
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
// Nothing.
|
||||
};
|
||||
xhr.open("get", theURL, true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
window.addEventListener("dog", do_xhr, true);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -493,6 +493,11 @@ this.PermissionsTable = { geolocation: {
|
||||
trusted: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"tv": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "mozilla/dom/ServiceWorkerContainer.h"
|
||||
#include "mozilla/dom/Telephony.h"
|
||||
#include "mozilla/dom/Voicemail.h"
|
||||
#include "mozilla/dom/TVManager.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "nsISiteSpecificUserAgent.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
@ -173,6 +174,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileMessageManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoicemail)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
|
||||
#ifdef MOZ_B2G_RIL
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
|
||||
@ -256,6 +258,10 @@ Navigator::Invalidate()
|
||||
mVoicemail = nullptr;
|
||||
}
|
||||
|
||||
if (mTVManager) {
|
||||
mTVManager = nullptr;
|
||||
}
|
||||
|
||||
if (mConnection) {
|
||||
mConnection->Shutdown();
|
||||
mConnection = nullptr;
|
||||
@ -1582,6 +1588,19 @@ Navigator::GetMozTelephony(ErrorResult& aRv)
|
||||
return mTelephony;
|
||||
}
|
||||
|
||||
TVManager*
|
||||
Navigator::GetTv()
|
||||
{
|
||||
if (!mTVManager) {
|
||||
if (!mWindow) {
|
||||
return nullptr;
|
||||
}
|
||||
mTVManager = TVManager::Create(mWindow);
|
||||
}
|
||||
|
||||
return mTVManager;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
already_AddRefed<Promise>
|
||||
Navigator::GetMobileIdAssertion(const MobileIdOptions& aOptions,
|
||||
@ -2326,6 +2345,37 @@ Navigator::HasMobileIdSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Navigator::HasTVSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
JS::Rooted<JSObject*> global(aCx, aGlobal);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just for testing, we can enable TV for any kind of app.
|
||||
if (Preferences::GetBool("dom.testing.tv_enabled_for_hosted_apps", false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIDocument* doc = win->GetExtantDoc();
|
||||
if (!doc || !doc->NodePrincipal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIPrincipal* principal = doc->NodePrincipal();
|
||||
uint16_t status;
|
||||
if (NS_FAILED(principal->GetAppStatus(&status))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only support TV Manager API for certified apps for now.
|
||||
return status == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsPIDOMWindow>
|
||||
Navigator::GetWindowFromGlobal(JSObject* aGlobal)
|
||||
|
@ -93,6 +93,7 @@ class PowerManager;
|
||||
class CellBroadcast;
|
||||
class Telephony;
|
||||
class Voicemail;
|
||||
class TVManager;
|
||||
|
||||
namespace time {
|
||||
class TimeManager;
|
||||
@ -223,6 +224,7 @@ public:
|
||||
MobileMessageManager* GetMozMobileMessage();
|
||||
Telephony* GetMozTelephony(ErrorResult& aRv);
|
||||
Voicemail* GetMozVoicemail(ErrorResult& aRv);
|
||||
TVManager* GetTv();
|
||||
network::Connection* GetConnection(ErrorResult& aRv);
|
||||
nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
|
||||
MediaDevices* GetMediaDevices(ErrorResult& aRv);
|
||||
@ -305,6 +307,8 @@ public:
|
||||
static bool HasMobileIdSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
#endif
|
||||
|
||||
static bool HasTVSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetWindow();
|
||||
@ -334,6 +338,7 @@ private:
|
||||
nsRefPtr<MobileMessageManager> mMobileMessageManager;
|
||||
nsRefPtr<Telephony> mTelephony;
|
||||
nsRefPtr<Voicemail> mVoicemail;
|
||||
nsRefPtr<TVManager> mTVManager;
|
||||
nsRefPtr<network::Connection> mConnection;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
nsRefPtr<MobileConnectionArray> mMobileConnections;
|
||||
|
@ -4491,13 +4491,19 @@ nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::HasNonEmptyTextContent(nsINode* aNode)
|
||||
nsContentUtils::HasNonEmptyTextContent(nsINode* aNode,
|
||||
TextContentDiscoverMode aDiscoverMode)
|
||||
{
|
||||
for (nsIContent* child = aNode->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (child->IsNodeOfType(nsINode::eTEXT) &&
|
||||
child->TextLength() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aDiscoverMode == eRecurseIntoChildren &&
|
||||
HasNonEmptyTextContent(child, aDiscoverMode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1240,12 +1240,19 @@ public:
|
||||
nsAString& aResult, const mozilla::fallible_t&);
|
||||
|
||||
/**
|
||||
* Utility method that checks if a given node has any non-empty
|
||||
* children.
|
||||
* NOTE! This method does not descend recursivly into elements.
|
||||
* Though it would be easy to make it so if needed
|
||||
* Utility method that checks if a given node has any non-empty children. This
|
||||
* method does not descend recursively into children by default.
|
||||
*
|
||||
* @param aDiscoverMode Set to eRecurseIntoChildren to descend recursively
|
||||
* into children.
|
||||
*/
|
||||
static bool HasNonEmptyTextContent(nsINode* aNode);
|
||||
enum TextContentDiscoverMode MOZ_ENUM_TYPE(uint8_t) {
|
||||
eRecurseIntoChildren, eDontRecurseIntoChildren
|
||||
};
|
||||
|
||||
static bool HasNonEmptyTextContent(
|
||||
nsINode* aNode,
|
||||
TextContentDiscoverMode aDiscoverMode = eDontRecurseIntoChildren);
|
||||
|
||||
/**
|
||||
* Delete strings allocated for nsContentList matches
|
||||
|
@ -707,6 +707,8 @@ GK_ATOM(onconnected, "onconnected")
|
||||
GK_ATOM(onconnecting, "onconnecting")
|
||||
GK_ATOM(oncontextmenu, "oncontextmenu")
|
||||
GK_ATOM(oncopy, "oncopy")
|
||||
GK_ATOM(oncurrentchannelchanged, "oncurrentchannelchanged")
|
||||
GK_ATOM(oncurrentsourcechanged, "oncurrentsourcechanged")
|
||||
GK_ATOM(oncut, "oncut")
|
||||
GK_ATOM(ondatachange, "ondatachange")
|
||||
GK_ATOM(ondataerror, "ondataerror")
|
||||
@ -746,6 +748,7 @@ GK_ATOM(ondragleave, "ondragleave")
|
||||
GK_ATOM(ondragover, "ondragover")
|
||||
GK_ATOM(ondragstart, "ondragstart")
|
||||
GK_ATOM(ondrop, "ondrop")
|
||||
GK_ATOM(oneitbroadcasted, "oneitbroadcasted")
|
||||
GK_ATOM(onenabled, "onenabled")
|
||||
GK_ATOM(onenterpincodereq, "onenterpincodereq")
|
||||
GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
|
||||
@ -848,6 +851,7 @@ GK_ATOM(onreset, "onreset")
|
||||
GK_ATOM(onresuming, "onresuming")
|
||||
GK_ATOM(onresize, "onresize")
|
||||
GK_ATOM(onrtchange, "onrtchange")
|
||||
GK_ATOM(onscanningstatechanged, "onscanningstatechanged")
|
||||
GK_ATOM(onscostatuschanged, "onscostatuschanged")
|
||||
GK_ATOM(onscroll, "onscroll")
|
||||
GK_ATOM(onselect, "onselect")
|
||||
|
@ -202,6 +202,7 @@
|
||||
#include "prrng.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "TimeChangeObserver.h"
|
||||
#include "TouchCaret.h"
|
||||
#include "mozilla/dom/AudioContext.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
|
||||
@ -9357,6 +9358,24 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, in
|
||||
SelectionChangeEventInit init;
|
||||
init.mBubbles = true;
|
||||
if (aSel) {
|
||||
bool isTouchCaretVisible = false;
|
||||
bool isCollapsed = aSel->Collapsed();
|
||||
|
||||
nsIPresShell *shell = mDoc->GetShell();
|
||||
if (shell) {
|
||||
nsRefPtr<TouchCaret> touchCaret = shell->GetTouchCaret();
|
||||
if (touchCaret) {
|
||||
isTouchCaretVisible = touchCaret->GetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch selection change events when touch caret is visible even if selection
|
||||
// is collapsed because it could be the shortcut mode, otherwise ignore this
|
||||
// UpdateCommands
|
||||
if (isCollapsed && !isTouchCaretVisible) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Selection* selection = static_cast<Selection*>(aSel);
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
nsLayoutUtils::RectAccumulator accumulator;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#ifdef MOZ_B2G
|
||||
@ -26,6 +27,7 @@
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsDOMCID.h"
|
||||
#include "nsError.h"
|
||||
#include "nsGkAtoms.h"
|
||||
@ -977,6 +979,47 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
||||
return result;
|
||||
}
|
||||
|
||||
nsIDocShell*
|
||||
EventListenerManager::GetDocShellForTarget()
|
||||
{
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
|
||||
nsIDocument* doc = nullptr;
|
||||
nsIDocShell* docShell = nullptr;
|
||||
|
||||
if (node) {
|
||||
doc = node->OwnerDoc();
|
||||
if (!doc->GetDocShell()) {
|
||||
bool ignore;
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(doc->GetScriptHandlingObject(ignore));
|
||||
if (window) {
|
||||
doc = window->GetExtantDoc();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
doc = window->GetExtantDoc();
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc) {
|
||||
nsCOMPtr<DOMEventTargetHelper> helper(do_QueryInterface(mTarget));
|
||||
if (helper) {
|
||||
nsPIDOMWindow* window = helper->GetOwner();
|
||||
if (window) {
|
||||
doc = window->GetExtantDoc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
docShell = doc->GetDocShell();
|
||||
}
|
||||
|
||||
return docShell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes a check for event listeners and processing by them if they exist.
|
||||
* @param an event listener
|
||||
@ -1028,10 +1071,28 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe add a marker to the docshell's timeline, but only
|
||||
// bother with all the logic if some docshell is recording.
|
||||
nsCOMPtr<nsIDocShell> docShell;
|
||||
if (mIsMainThreadELM &&
|
||||
nsDocShell::gProfileTimelineRecordingsCount > 0 &&
|
||||
listener->mListenerType != Listener::eNativeListener) {
|
||||
docShell = GetDocShellForTarget();
|
||||
if (docShell) {
|
||||
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
|
||||
ds->AddProfileTimelineMarker("DOMEvent", TRACING_INTERVAL_START);
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent,
|
||||
aCurrentTarget))) {
|
||||
aEvent->mFlags.mExceptionHasBeenRisen = true;
|
||||
}
|
||||
|
||||
if (docShell) {
|
||||
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
|
||||
ds->AddProfileTimelineMarker("DOMEvent", TRACING_INTERVAL_END);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
class nsIDocShell;
|
||||
class nsIDOMEvent;
|
||||
class nsIEventListenerInfo;
|
||||
class nsIScriptContext;
|
||||
@ -420,6 +421,8 @@ protected:
|
||||
nsIDOMEvent* aDOMEvent,
|
||||
dom::EventTarget* aCurrentTarget);
|
||||
|
||||
nsIDocShell* GetDocShellForTarget();
|
||||
|
||||
/**
|
||||
* Compile the "inline" event listener for aListener. The
|
||||
* body of the listener can be provided in aBody; if this is null we
|
||||
|
@ -130,6 +130,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/docshell/base',
|
||||
'/dom/base',
|
||||
'/dom/html',
|
||||
'/dom/settings',
|
||||
|
@ -297,6 +297,10 @@ const kEventConstructors = {
|
||||
return new MozNFCPeerEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozNFCTagEvent: { create: function (aName, aProps) {
|
||||
return new MozNFCTagEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozOtaStatusEvent: { create: function (aName, aProps) {
|
||||
return new MozOtaStatusEvent(aName, aProps);
|
||||
},
|
||||
@ -468,6 +472,38 @@ const kEventConstructors = {
|
||||
return new TransitionEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVCurrentChannelChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVCurrentChannelChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVCurrentProgramChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVCurrentProgramChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVCurrentSourceChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVCurrentSourceChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVEITBroadcastedEvent: { create: function (aName, aProps) {
|
||||
return new TVEITBroadcastedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVParentalControlChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVParentalControlChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVProtectionStateChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVProtectionStateChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVScanningStateChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVScanningStateChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
TVTunerChangedEvent: { create: function (aName, aProps) {
|
||||
return new TVTunerChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
UIEvent: { create: function (aName, aProps) {
|
||||
return new UIEvent(aName, aProps);
|
||||
},
|
||||
|
@ -93,6 +93,7 @@ DIRS += [
|
||||
'promise',
|
||||
'smil',
|
||||
'telephony',
|
||||
'tv',
|
||||
'voicemail',
|
||||
'inputmethod',
|
||||
'webidl',
|
||||
|
@ -28,6 +28,8 @@ GetNetUtilsLibHandle()
|
||||
return sNetUtilsLib;
|
||||
}
|
||||
|
||||
mozilla::Mutex NetUtils::sIfcMutex("NetUtils::sIfcMutex");
|
||||
|
||||
// static
|
||||
void*
|
||||
NetUtils::GetSharedLibrary()
|
||||
@ -62,21 +64,20 @@ DEFINE_DLFUNC(ifc_remove_default_route, int32_t, const char*)
|
||||
DEFINE_DLFUNC(dhcp_stop, int32_t, const char*)
|
||||
|
||||
NetUtils::NetUtils()
|
||||
: mIfcMutex("NetUtils::mIfcMutex")
|
||||
{
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_enable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_enable)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_enable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_disable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_disable)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_disable(ifname);
|
||||
}
|
||||
|
||||
@ -88,7 +89,7 @@ int32_t NetUtils::do_ifc_configure(const char *ifname,
|
||||
in_addr_t dns2)
|
||||
{
|
||||
USE_DLFUNC(ifc_configure)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
|
||||
return ret;
|
||||
}
|
||||
@ -97,7 +98,7 @@ int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
|
||||
const int32_t resetMask)
|
||||
{
|
||||
USE_DLFUNC(ifc_reset_connections)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_reset_connections(ifname, resetMask);
|
||||
}
|
||||
|
||||
@ -105,7 +106,7 @@ int32_t NetUtils::do_ifc_set_default_route(const char *ifname,
|
||||
in_addr_t gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_set_default_route)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_set_default_route(ifname, gateway);
|
||||
}
|
||||
|
||||
@ -115,7 +116,7 @@ int32_t NetUtils::do_ifc_add_route(const char *ifname,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_add_route)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_add_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
@ -125,21 +126,21 @@ int32_t NetUtils::do_ifc_remove_route(const char *ifname,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_route)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_host_routes(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_host_routes)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_host_routes(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_default_route(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_default_route)
|
||||
mozilla::MutexAutoLock lock(mIfcMutex);
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_default_route(ifname);
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ public:
|
||||
static int32_t SdkVersion();
|
||||
|
||||
private:
|
||||
mozilla::Mutex mIfcMutex;
|
||||
static mozilla::Mutex sIfcMutex;
|
||||
};
|
||||
|
||||
// Defines a function type with the right arguments and return type.
|
||||
|
@ -408,12 +408,19 @@ NfcContentHelper.prototype = {
|
||||
break;
|
||||
case "NFC:DOMEvent":
|
||||
switch (result.event) {
|
||||
case NFC.NFC_PEER_EVENT_READY:
|
||||
case NFC.PEER_EVENT_READY:
|
||||
this.eventTarget.notifyPeerReady(result.sessionToken);
|
||||
break;
|
||||
case NFC.NFC_PEER_EVENT_LOST:
|
||||
case NFC.PEER_EVENT_LOST:
|
||||
this.eventTarget.notifyPeerLost(result.sessionToken);
|
||||
break;
|
||||
case NFC.TAG_EVENT_FOUND:
|
||||
let event = new NfcTagEvent(result.techList);
|
||||
this.eventTarget.notifyTagFound(result.sessionToken, event, result.records);
|
||||
break;
|
||||
case NFC.TAG_EVENT_LOST:
|
||||
this.eventTarget.notifyTagLost(result.sessionToken);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -462,6 +469,15 @@ NfcContentHelper.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function NfcTagEvent(techList) {
|
||||
this.techList = techList;
|
||||
}
|
||||
NfcTagEvent.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcTagEvent]),
|
||||
|
||||
techList: null
|
||||
};
|
||||
|
||||
if (NFC_ENABLED) {
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
|
||||
}
|
||||
|
@ -247,10 +247,25 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
|
||||
// Remember the target that receives onpeerready.
|
||||
this.currentPeer = target;
|
||||
this.notifyDOMEvent(target, {event: NFC.NFC_PEER_EVENT_READY,
|
||||
this.notifyDOMEvent(target, {event: NFC.PEER_EVENT_READY,
|
||||
sessionToken: sessionToken});
|
||||
},
|
||||
|
||||
onTagFound: function onTagFound(message) {
|
||||
message.event = NFC.TAG_EVENT_FOUND;
|
||||
for (let target of this.eventTargets) {
|
||||
this.notifyDOMEvent(target, message);
|
||||
}
|
||||
delete message.event;
|
||||
},
|
||||
|
||||
onTagLost: function onTagLost(sessionToken) {
|
||||
for (let target of this.eventTargets) {
|
||||
this.notifyDOMEvent(target, {event: NFC.TAG_EVENT_LOST,
|
||||
sessionToken: sessionToken});
|
||||
}
|
||||
},
|
||||
|
||||
onPeerLost: function onPeerLost(sessionToken) {
|
||||
if (!this.currentPeer) {
|
||||
// The target is already killed.
|
||||
@ -259,7 +274,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
|
||||
// For peerlost, the message is delievered to the target which
|
||||
// onpeerready has been called before.
|
||||
this.notifyDOMEvent(this.currentPeer, {event: NFC.NFC_PEER_EVENT_LOST,
|
||||
this.notifyDOMEvent(this.currentPeer, {event: NFC.PEER_EVENT_LOST,
|
||||
sessionToken: sessionToken});
|
||||
this.currentPeer = null;
|
||||
},
|
||||
@ -526,8 +541,13 @@ Nfc.prototype = {
|
||||
SessionHelper.registerSession(message.sessionId, message.techList);
|
||||
|
||||
// Do not expose the actual session to the content
|
||||
let sessionId = message.sessionId;
|
||||
delete message.sessionId;
|
||||
|
||||
if (!SessionHelper.isP2PSession(sessionId)) {
|
||||
gMessageManager.onTagFound(message);
|
||||
}
|
||||
|
||||
gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", message);
|
||||
break;
|
||||
case "TechLostNotification":
|
||||
@ -537,6 +557,8 @@ Nfc.prototype = {
|
||||
message.sessionToken = SessionHelper.getToken(message.sessionId);
|
||||
if (SessionHelper.isP2PSession(message.sessionId)) {
|
||||
gMessageManager.onPeerLost(message.sessionToken);
|
||||
} else {
|
||||
gMessageManager.onTagLost(message.sessionToken);
|
||||
}
|
||||
|
||||
SessionHelper.unregisterSession(message.sessionId);
|
||||
|
@ -86,8 +86,10 @@ this.TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
|
||||
|
||||
this.SETTING_NFC_DEBUG = "nfc.debugging.enabled";
|
||||
|
||||
this.NFC_PEER_EVENT_READY = 0x01;
|
||||
this.NFC_PEER_EVENT_LOST = 0x02;
|
||||
this.PEER_EVENT_READY = 0x01;
|
||||
this.PEER_EVENT_LOST = 0x02;
|
||||
this.TAG_EVENT_FOUND = 0x03;
|
||||
this.TAG_EVENT_LOST = 0x04;
|
||||
|
||||
// Allow this file to be imported via Components.utils.import().
|
||||
this.EXPORTED_SYMBOLS = Object.keys(this);
|
||||
|
@ -7,24 +7,52 @@
|
||||
|
||||
interface nsIVariant;
|
||||
|
||||
[scriptable, uuid(e81cc1ac-6f0b-4581-a9fb-7ee47ed0158e)]
|
||||
[scriptable, uuid(44eb02f4-6eba-4c12-8ed1-c142513776c9)]
|
||||
interface nsINfcTagEvent : nsISupports
|
||||
{
|
||||
readonly attribute nsIVariant techList;
|
||||
};
|
||||
|
||||
[scriptable, uuid(8d77d653-f76a-4a21-ae3f-c4cc6074d8ec)]
|
||||
interface nsINfcDOMEventTarget : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to notify tagfound.
|
||||
*
|
||||
* @param sessionToken
|
||||
* SessionToken received from parent process
|
||||
* @param event
|
||||
* nsINfcTagFoundEvent received from parent process.
|
||||
* @param ndefRecords
|
||||
* NDEF records pre-read during tag-discovered.
|
||||
*/
|
||||
void notifyTagFound(in DOMString sessionToken,
|
||||
in nsINfcTagEvent event,
|
||||
in nsIVariant ndefRecords);
|
||||
|
||||
/**
|
||||
* Callback function used to notify taglost.
|
||||
*
|
||||
* @param sessionToken
|
||||
* SessionToken received from parent process
|
||||
*/
|
||||
void notifyTagLost(in DOMString sessionToken);
|
||||
|
||||
/**
|
||||
* Callback function used to notify peerready.
|
||||
*
|
||||
* @param sessionToken
|
||||
* SessionToken received from Chrome process
|
||||
* SessionToken received from parent process
|
||||
*/
|
||||
void notifyPeerReady(in DOMString sessionToken);
|
||||
void notifyPeerReady(in DOMString sessionToken);
|
||||
|
||||
/**
|
||||
* Callback function used to notify peerlost.
|
||||
*
|
||||
* @param sessionToken
|
||||
* SessionToken received from Chrome process
|
||||
* SessionToken received from parent process
|
||||
*/
|
||||
void notifyPeerLost(in DOMString sessionToken);
|
||||
void notifyPeerLost(in DOMString sessionToken);
|
||||
};
|
||||
|
||||
[scriptable, uuid(cb9c934d-a7fa-422b-bcc1-4ac39741e6ec)]
|
||||
@ -44,23 +72,23 @@ interface nsINfcContentHelper : nsISupports
|
||||
nsIDOMDOMRequest connect(in nsIDOMWindow window, in unsigned long techType, in DOMString sessionToken);
|
||||
nsIDOMDOMRequest close(in nsIDOMWindow window, in DOMString sessionToken);
|
||||
|
||||
/**
|
||||
* Initiate Send file operation
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param blob
|
||||
* Raw data of the file to be sent. This object represents a file-like
|
||||
* (nsIDOMFile) object of immutable, raw data. The blob data needs
|
||||
* to be 'object wrapped' before calling this interface.
|
||||
*
|
||||
* @param sessionToken
|
||||
* Current token
|
||||
*
|
||||
* Returns DOMRequest, if initiation of send file operation is successful
|
||||
* then 'onsuccess' is called else 'onerror'
|
||||
*/
|
||||
/**
|
||||
* Initiate send file operation.
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param blob
|
||||
* Raw data of the file to be sent. This object represents a file-like
|
||||
* (nsIDOMFile) object of immutable, raw data. The blob data needs
|
||||
* to be 'object wrapped' before calling this interface.
|
||||
*
|
||||
* @param sessionToken
|
||||
* Current token
|
||||
*
|
||||
* Returns DOMRequest, if initiation of send file operation is successful
|
||||
* then 'onsuccess' is called else 'onerror'
|
||||
*/
|
||||
nsIDOMDOMRequest sendFile(in nsIDOMWindow window,
|
||||
in jsval blob,
|
||||
in DOMString sessionToken);
|
||||
@ -72,66 +100,66 @@ interface nsINfcContentHelper : nsISupports
|
||||
*/
|
||||
void registerEventTarget(in nsINfcDOMEventTarget target);
|
||||
|
||||
/**
|
||||
* Register the given application id with Chrome process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be registered
|
||||
*/
|
||||
/**
|
||||
* Register the given application id with parent process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be registered
|
||||
*/
|
||||
void registerTargetForPeerReady(in nsIDOMWindow window,
|
||||
in unsigned long appId);
|
||||
|
||||
/**
|
||||
* Unregister the given application id with Chrome process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be registered
|
||||
*/
|
||||
/**
|
||||
* Unregister the given application id with parent process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be registered
|
||||
*/
|
||||
void unregisterTargetForPeerReady(in nsIDOMWindow window,
|
||||
in unsigned long appId);
|
||||
|
||||
/**
|
||||
* Checks if the given application's id is a registered peer target (with the Chrome process)
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be updated with Chrome process
|
||||
*
|
||||
* Returns DOMRequest, if appId is registered then 'onsuccess' is called else 'onerror'
|
||||
*/
|
||||
/**
|
||||
* Checks if the given application's id is a registered peer target (with the parent process)
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID to be updated with parent process
|
||||
*
|
||||
* Returns DOMRequest, if appId is registered then 'onsuccess' is called else 'onerror'
|
||||
*/
|
||||
nsIDOMDOMRequest checkP2PRegistration(in nsIDOMWindow window, in unsigned long appId);
|
||||
|
||||
/**
|
||||
* Notify the Chrome process that user has accepted to share nfc message on P2P UI
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID that is capable of handling NFC_EVENT_PEER_READY event
|
||||
*/
|
||||
/**
|
||||
* Notify the parent process that user has accepted to share nfc message on P2P UI
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param appId
|
||||
* Application ID that is capable of handling NFC_EVENT_PEER_READY event
|
||||
*/
|
||||
void notifyUserAcceptedP2P(in nsIDOMWindow window, in unsigned long appId);
|
||||
|
||||
/**
|
||||
* Notify the status of sendFile operation to Chrome process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param status
|
||||
* Status of sendFile operation
|
||||
*
|
||||
* @param requestId
|
||||
* Request ID of SendFile DOM Request
|
||||
*/
|
||||
/**
|
||||
* Notify the status of sendFile operation to parent process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param status
|
||||
* Status of sendFile operation
|
||||
*
|
||||
* @param requestId
|
||||
* Request ID of SendFile DOM Request
|
||||
*/
|
||||
void notifySendFileStatus(in nsIDOMWindow window,
|
||||
in octet status,
|
||||
in DOMString requestId);
|
||||
|
101
dom/nfc/nsNfc.js
@ -125,6 +125,11 @@ mozNfc.prototype = {
|
||||
init: function init(aWindow) {
|
||||
debug("mozNfc init called");
|
||||
this._window = aWindow;
|
||||
this.defineEventHandlerGetterSetter("ontagfound");
|
||||
this.defineEventHandlerGetterSetter("ontaglost");
|
||||
this.defineEventHandlerGetterSetter("onpeerready");
|
||||
this.defineEventHandlerGetterSetter("onpeerlost");
|
||||
|
||||
if (this._nfcContentHelper) {
|
||||
this._nfcContentHelper.init(aWindow);
|
||||
}
|
||||
@ -162,14 +167,6 @@ mozNfc.prototype = {
|
||||
return this._nfcContentHelper.powerOff(this._window);
|
||||
},
|
||||
|
||||
getNFCTag: function getNFCTag(sessionToken) {
|
||||
let obj = new MozNFCTag(this._window, sessionToken);
|
||||
if (this._nfcContentHelper.checkSessionToken(sessionToken)) {
|
||||
return this._window.MozNFCTag._create(this._window, obj);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getNFCPeer: function getNFCPeer(sessionToken) {
|
||||
if (!sessionToken || !this._nfcContentHelper.checkSessionToken(sessionToken)) {
|
||||
return null;
|
||||
@ -184,22 +181,15 @@ mozNfc.prototype = {
|
||||
return this.nfcObject.contentObject;
|
||||
},
|
||||
|
||||
// get/set onpeerready
|
||||
get onpeerready() {
|
||||
return this.__DOM_IMPL__.getEventHandler("onpeerready");
|
||||
},
|
||||
|
||||
set onpeerready(handler) {
|
||||
this.__DOM_IMPL__.setEventHandler("onpeerready", handler);
|
||||
},
|
||||
|
||||
// get/set onpeerlost
|
||||
get onpeerlost() {
|
||||
return this.__DOM_IMPL__.getEventHandler("onpeerlost");
|
||||
},
|
||||
|
||||
set onpeerlost(handler) {
|
||||
this.__DOM_IMPL__.setEventHandler("onpeerlost", handler);
|
||||
defineEventHandlerGetterSetter: function defineEventHandlerGetterSetter(name) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function get() {
|
||||
return this.__DOM_IMPL__.getEventHandler(name);
|
||||
},
|
||||
set: function set(handler) {
|
||||
this.__DOM_IMPL__.setEventHandler(name, handler);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
eventListenerWasAdded: function(eventType) {
|
||||
@ -220,6 +210,54 @@ mozNfc.prototype = {
|
||||
this._nfcContentHelper.unregisterTargetForPeerReady(this._window, appId);
|
||||
},
|
||||
|
||||
notifyTagFound: function notifyTagFound(sessionToken, event, records) {
|
||||
if (this.hasDeadWrapper()) {
|
||||
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.checkPermissions(["nfc-read", "nfc-write"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tag = new MozNFCTag(this._window, sessionToken);
|
||||
let tagContentObj = this._window.MozNFCTag._create(this._window, tag);
|
||||
|
||||
let length = records ? records.length : 0;
|
||||
let ndefRecords = records ? [] : null;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let record = records[i];
|
||||
ndefRecords.push(new this._window.MozNDEFRecord({tnf: record.tnf,
|
||||
type: record.type,
|
||||
id: record.id,
|
||||
payload: record.payload}));
|
||||
}
|
||||
|
||||
let eventData = {
|
||||
"tag": tagContentObj,
|
||||
"ndefRecords": ndefRecords
|
||||
};
|
||||
|
||||
debug("fire ontagfound " + sessionToken);
|
||||
let tagEvent = new this._window.MozNFCTagEvent("tagfound", eventData);
|
||||
this.__DOM_IMPL__.dispatchEvent(tagEvent);
|
||||
},
|
||||
|
||||
notifyTagLost: function notifyTagLost(sessionToken) {
|
||||
if (this.hasDeadWrapper()) {
|
||||
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.checkPermissions(["nfc-read", "nfc-write"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug("fire ontaglost " + sessionToken);
|
||||
let event = new this._window.Event("taglost");
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
|
||||
notifyPeerReady: function notifyPeerReady(sessionToken) {
|
||||
if (this.hasDeadWrapper()) {
|
||||
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
|
||||
@ -259,6 +297,21 @@ mozNfc.prototype = {
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
|
||||
checkPermissions: function checkPermissions(perms) {
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
for (let perm of perms) {
|
||||
let permValue =
|
||||
Services.perms.testExactPermissionFromPrincipal(principal, perm);
|
||||
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
return true;
|
||||
} else {
|
||||
debug("doesn't have " + perm + " permission.");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
hasDeadWrapper: function hasDeadWrapper() {
|
||||
return Cu.isDeadWrapper(this._window) || Cu.isDeadWrapper(this.__DOM_IMPL__);
|
||||
},
|
||||
|
@ -191,18 +191,6 @@ function testPeerInvalidToken() {
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Added for completeness in Bug 1042651,
|
||||
* TODO: remove once Bug 963531 lands
|
||||
*/
|
||||
function testTagInvalidToken() {
|
||||
log("testTagInvalidToken");
|
||||
let tag = nfc.getNFCTag("fakeSessionToken");
|
||||
is(tag, null, "NFCTag should be null on wrong session token");
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
testPeerReady,
|
||||
testGetNFCPeer,
|
||||
@ -210,8 +198,7 @@ let tests = [
|
||||
testPeerLostShouldBeCalled,
|
||||
testPeerLostShouldNotBeCalled,
|
||||
testPeerShouldThrow,
|
||||
testPeerInvalidToken,
|
||||
testTagInvalidToken
|
||||
testPeerInvalidToken
|
||||
];
|
||||
|
||||
SpecialPowers.pushPermissions(
|
||||
|
@ -55,7 +55,12 @@ function SettingsLock(aSettingsManager) {
|
||||
"Settings:Clear:OK", "Settings:Clear:KO",
|
||||
"Settings:Set:OK", "Settings:Set:KO",
|
||||
"Settings:Finalize:OK", "Settings:Finalize:KO"]);
|
||||
this.sendMessage("Settings:CreateLock", {lockID: this._id, isServiceLock: false});
|
||||
let createLockPayload = {
|
||||
lockID: this._id,
|
||||
isServiceLock: false,
|
||||
windowID: this._settingsManager.innerWindowID
|
||||
};
|
||||
this.sendMessage("Settings:CreateLock", createLockPayload);
|
||||
Services.tm.currentThread.dispatch(this._closeHelper.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
// We only want to file closeHelper once per set of receiveMessage calls.
|
||||
@ -255,7 +260,6 @@ function SettingsManager() {
|
||||
this._callbacks = null;
|
||||
this._isRegistered = false;
|
||||
this._locks = [];
|
||||
this._principal = null;
|
||||
}
|
||||
|
||||
SettingsManager.prototype = {
|
||||
@ -376,17 +380,18 @@ SettingsManager.prototype = {
|
||||
if (DEBUG) debug("SettingsManager init");
|
||||
mrm.registerStrongReporter(this);
|
||||
cpmm.addMessageListener("Settings:Change:Return:OK", this);
|
||||
this._window = aWindow;
|
||||
this._principal = this._window.document.nodePrincipal;
|
||||
Services.obs.addObserver(this, "dom-window-destroyed", false);
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
||||
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
this.innerWindowID = util.currentInnerWindowID;
|
||||
this._window = aWindow;
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "dom-window-destroyed") {
|
||||
let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
if (window == this._window) {
|
||||
if (DEBUG) debug("Topic: " + aTopic);
|
||||
if (aTopic === "inner-window-destroyed") {
|
||||
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (wId === this.innerWindowID) {
|
||||
if (DEBUG) debug("Received: inner-window-destroyed for valid innerWindowID=" + wId + ", cleanup.");
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
@ -417,21 +422,11 @@ SettingsManager.prototype = {
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
Services.obs.removeObserver(this, "dom-window-destroyed");
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
cpmm.removeMessageListener("Settings:Change:Return:OK", this);
|
||||
mrm.unregisterStrongReporter(this);
|
||||
// At this point, the window is dying, so there's nothing left
|
||||
// that we could do with our lock. Go ahead and run finalize on
|
||||
// it to make sure changes are commited.
|
||||
for (let i = 0; i < this._locks.length; ++i) {
|
||||
if (DEBUG) debug("Lock alive at destroy, finalizing: " + this._locks[i]);
|
||||
cpmm.sendAsyncMessage("Settings:Finalize",
|
||||
{lockID: this._locks[i]},
|
||||
undefined,
|
||||
this._principal);
|
||||
}
|
||||
this.innerWindowID = null;
|
||||
this._window = null;
|
||||
this._innerWindowID = null;
|
||||
},
|
||||
|
||||
classID: Components.ID("{c40b1c70-00fb-11e2-a21f-0800200c9a66}"),
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
||||
|
||||
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
|
||||
const kInnerWindowDestroyed = "inner-window-destroyed";
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kSettingsReadSuffix = "-read";
|
||||
const kSettingsWriteSuffix = "-write";
|
||||
@ -71,12 +72,14 @@ let SettingsPermissions = {
|
||||
};
|
||||
|
||||
|
||||
function SettingsLockInfo(aDB, aMsgMgr, aLockID, aIsServiceLock) {
|
||||
function SettingsLockInfo(aDB, aMsgMgr, aLockID, aIsServiceLock, aWindowID) {
|
||||
return {
|
||||
// ID Shared with the object on the child side
|
||||
lockID: aLockID,
|
||||
// Is this a content lock or a settings service lock?
|
||||
isServiceLock: aIsServiceLock,
|
||||
// Which inner window ID
|
||||
windowID: aWindowID,
|
||||
// Tasks to be run once the lock is at the head of the queue
|
||||
tasks: [],
|
||||
// This is set to true once a transaction is ready to run, but is not at the
|
||||
@ -171,6 +174,7 @@ let SettingsRequestManager = {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
|
||||
Services.obs.addObserver(this, kInnerWindowDestroyed, false);
|
||||
},
|
||||
|
||||
_serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
|
||||
@ -648,7 +652,7 @@ let SettingsRequestManager = {
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (DEBUG) debug("observe");
|
||||
if (DEBUG) debug("observe: " + aTopic);
|
||||
switch (aTopic) {
|
||||
case kXpcomShutdownObserverTopic:
|
||||
this.messages.forEach((function(msgName) {
|
||||
@ -657,6 +661,12 @@ let SettingsRequestManager = {
|
||||
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
|
||||
ppmm = null;
|
||||
break;
|
||||
|
||||
case kInnerWindowDestroyed:
|
||||
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
this.forceFinalizeChildLocksNonOOP(wId);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) debug("Wrong observer topic: " + aTopic);
|
||||
break;
|
||||
@ -745,42 +755,59 @@ let SettingsRequestManager = {
|
||||
}
|
||||
},
|
||||
|
||||
removeMessageManager: function(aMsgMgr, aPrincipal) {
|
||||
if (DEBUG) debug("Removing message manager");
|
||||
hasLockFinalizeTask: function(lock) {
|
||||
// Go in reverse order because finalize should be the last one
|
||||
for (let task_index = lock.tasks.length; task_index >= 0; task_index--) {
|
||||
if (lock.tasks[task_index]
|
||||
&& lock.tasks[task_index].operation === "finalize") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
enqueueForceFinalize: function(lock, principal) {
|
||||
if (!this.hasLockFinalizeTask(lock)) {
|
||||
if (DEBUG) debug("Alive lock has pending tasks: " + lock.lockID);
|
||||
this.queueTask("finalize", {lockID: lock.lockID}, principal).then(
|
||||
function() {
|
||||
if (DEBUG) debug("Alive lock " + lockId + " succeeded to force-finalize");
|
||||
},
|
||||
function(error) {
|
||||
if (DEBUG) debug("Alive lock " + lockId + " failed to force-finalize due to error: " + error);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
forceFinalizeChildLocksNonOOP: function(windowId) {
|
||||
if (DEBUG) debug("Forcing finalize on child locks, non OOP");
|
||||
|
||||
for (let lockId of Object.keys(this.lockInfo)) {
|
||||
let lock = this.lockInfo[lockId];
|
||||
if (lock.windowID === windowId) {
|
||||
let principal = this.mmPrincipals.get(lock._mm);
|
||||
this.enqueueForceFinalize(lock, principal);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
forceFinalizeChildLocksOOP: function(aMsgMgr, aPrincipal) {
|
||||
if (DEBUG) debug("Forcing finalize on child locks, OOP");
|
||||
|
||||
let msgMgrPrincipal = this.mmPrincipals.get(aMsgMgr);
|
||||
this.removeObserver(aMsgMgr);
|
||||
|
||||
let lockIDs = Object.keys(this.lockInfo);
|
||||
for (let i in lockIDs) {
|
||||
let lockId = lockIDs[i];
|
||||
for (let lockId of Object.keys(this.lockInfo)) {
|
||||
let lock = this.lockInfo[lockId];
|
||||
if (lock._mm === aMsgMgr && msgMgrPrincipal === aPrincipal) {
|
||||
let is_finalizing = false;
|
||||
let task_index;
|
||||
// Go in reverse order because finalize should be the last one
|
||||
for (task_index = lock.tasks.length; task_index >= 0; task_index--) {
|
||||
if (lock.tasks[task_index]
|
||||
&& lock.tasks[task_index].operation === "finalize") {
|
||||
is_finalizing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_finalizing) {
|
||||
this.queueTask("finalize", {lockID: lockId}, aPrincipal).then(
|
||||
function() {
|
||||
if (DEBUG) debug("Lock " + lockId + " with dead message manager finalized");
|
||||
},
|
||||
function(error) {
|
||||
if (DEBUG) debug("Lock " + lockId + " with dead message manager NOT FINALIZED due to error: " + error);
|
||||
}
|
||||
);
|
||||
}
|
||||
this.enqueueForceFinalize(lock, aPrincipal);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage " + aMessage.name);
|
||||
if (DEBUG) debug("receiveMessage " + aMessage.name + ": " + JSON.stringify(aMessage.data));
|
||||
|
||||
let msg = aMessage.data;
|
||||
let mm = aMessage.target;
|
||||
@ -832,7 +859,7 @@ let SettingsRequestManager = {
|
||||
switch (aMessage.name) {
|
||||
case "child-process-shutdown":
|
||||
if (DEBUG) debug("Child process shutdown received.");
|
||||
this.removeMessageManager(mm, aMessage.principal);
|
||||
this.forceFinalizeChildLocksOOP(mm, aMessage.principal);
|
||||
break;
|
||||
case "Settings:RegisterForMessages":
|
||||
if (!SettingsPermissions.hasSomeReadPermission(aMessage.principal)) {
|
||||
@ -847,7 +874,7 @@ let SettingsRequestManager = {
|
||||
this.removeObserver(mm);
|
||||
break;
|
||||
case "Settings:CreateLock":
|
||||
if (DEBUG) debug("Received CreateLock for " + msg.lockID + " from " + aMessage.principal.origin);
|
||||
if (DEBUG) debug("Received CreateLock for " + msg.lockID + " from " + aMessage.principal.origin + " window: " + msg.windowID);
|
||||
// If we try to create a lock ID that collides with one
|
||||
// already in the system, consider it a security violation and
|
||||
// kill.
|
||||
@ -857,7 +884,11 @@ let SettingsRequestManager = {
|
||||
return;
|
||||
}
|
||||
this.settingsLockQueue.push(msg.lockID);
|
||||
this.lockInfo[msg.lockID] = SettingsLockInfo(this.settingsDB, mm, msg.lockID, msg.isServiceLock);
|
||||
this.lockInfo[msg.lockID] = SettingsLockInfo(this.settingsDB,
|
||||
mm,
|
||||
msg.lockID,
|
||||
msg.isServiceLock,
|
||||
msg.windowID);
|
||||
break;
|
||||
case "Settings:Get":
|
||||
if (DEBUG) debug("Received getRequest from " + msg.lockID);
|
||||
|
@ -109,6 +109,11 @@ typedef Tuple3<NetdCommand*, CommandChain*, CommandCallback> QueueData;
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define WARN_IF_FAILED(rv) do { \
|
||||
if (SUCCESS != rv) { \
|
||||
WARN("Error (%d) occurred in %s (%s:%d)", rv, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
static NetworkUtils* gNetworkUtils;
|
||||
static nsTArray<QueueData> gCommandQueue;
|
||||
@ -1388,7 +1393,7 @@ CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
|
||||
// Remove IPv4's default route.
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname)));
|
||||
// Remove IPv6's default route.
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
|
||||
WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
|
||||
}
|
||||
|
||||
uint32_t length = aOptions.mGateways.Length();
|
||||
@ -1444,9 +1449,9 @@ CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
|
||||
type == AF_INET ? "0.0.0.0" : "::",
|
||||
0, autoGateway.get()));
|
||||
WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
|
||||
type == AF_INET ? "0.0.0.0" : "::",
|
||||
0, autoGateway.get()));
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
@ -1543,7 +1548,7 @@ CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
}
|
||||
|
||||
// Remove default route.
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL));
|
||||
WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL));
|
||||
|
||||
// Remove subnet route.
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL));
|
||||
|
@ -533,6 +533,10 @@ this.ICC_EF_MWIS = 0x6fca;
|
||||
this.ICC_EF_CFIS = 0x6fcb;
|
||||
this.ICC_EF_SPDI = 0x6fcd;
|
||||
|
||||
// CPHS files to be supported
|
||||
this.ICC_EF_CPHS_INFO = 0x6f16; // CPHS Information
|
||||
this.ICC_EF_CPHS_MBN = 0x6f17; // Mailbox Numbers
|
||||
|
||||
// CSIM files
|
||||
this.ICC_EF_CSIM_IMSI_M = 0x6f22;
|
||||
this.ICC_EF_CSIM_CDMAHOME = 0x6f28;
|
||||
@ -1309,6 +1313,18 @@ this.GECKO_ICC_SERVICES = {
|
||||
ENHANCED_PHONEBOOK: 6,
|
||||
SPN: 17,
|
||||
SDN: 18
|
||||
},
|
||||
// @see B.3.1.1 CPHS Information in CPHS Phase 2:
|
||||
// Indicates which of the CPHS 'optional' data-fields are present in the SIM card:
|
||||
// EF_CPHS_CSP, EF_CPHS_SST, EF_CPHS_MBN, EF_CPHS_ONSF, EF_CPHS_INFO_NUM
|
||||
// Note: Mandatory EFs are: (B.3.1 Enhanced SIM Requirements)
|
||||
// EF_CPHS_CFF, EF_CPHS_VMI, EF_CPHS_ONS, EF_CPHS_INFO
|
||||
cphs: {
|
||||
CSP: 1,
|
||||
SST: 2,
|
||||
MBN: 3,
|
||||
ONSF: 4,
|
||||
INFO_NUM: 5
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12150,6 +12150,8 @@ ICCFileHelperObject.prototype = {
|
||||
case ICC_EF_OPL:
|
||||
case ICC_EF_PNN:
|
||||
case ICC_EF_GID1:
|
||||
case ICC_EF_CPHS_INFO:
|
||||
case ICC_EF_CPHS_MBN:
|
||||
return EF_PATH_MF_SIM + EF_PATH_DF_GSM;
|
||||
default:
|
||||
return null;
|
||||
@ -12176,6 +12178,12 @@ ICCFileHelperObject.prototype = {
|
||||
case ICC_EF_PNN:
|
||||
case ICC_EF_SMS:
|
||||
case ICC_EF_GID1:
|
||||
// CPHS spec was provided in 1997 based on SIM requirement, there is no
|
||||
// detailed info about how these ICC_EF_CPHS_XXX are allocated in USIM.
|
||||
// What we can do now is to follow what has been done in AOSP to have file
|
||||
// path equal to MF_SIM/DF_GSM.
|
||||
case ICC_EF_CPHS_INFO:
|
||||
case ICC_EF_CPHS_MBN:
|
||||
return EF_PATH_MF_SIM + EF_PATH_ADF_USIM;
|
||||
default:
|
||||
// The file ids in USIM phone book entries are decided by the
|
||||
@ -13101,7 +13109,18 @@ SimRecordHelperObject.prototype = {
|
||||
fetchSimRecords: function() {
|
||||
this.context.RIL.getIMSI();
|
||||
this.readAD();
|
||||
this.readSST();
|
||||
// CPHS was widely introduced in Europe during GSM(2G) era to provide easier
|
||||
// access to carrier's core service like voicemail, call forwarding, manual
|
||||
// PLMN selection, and etc.
|
||||
// Addition EF like EF_CPHS_MBN, EF_CPHS_CPHS_CFF, EF_CPHS_VWI, etc are
|
||||
// introduced to support these feature.
|
||||
// In USIM, the replancement of these EFs are provided. (EF_MBDN, EF_MWIS, ...)
|
||||
// However, some carriers in Europe still rely on these EFs.
|
||||
this.readCphsInfo(() => this.readSST(),
|
||||
(aErrorMsg) => {
|
||||
this.context.debug("Failed to read CPHS_INFO: " + aErrorMsg);
|
||||
this.readSST();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -13432,6 +13451,13 @@ SimRecordHelperObject.prototype = {
|
||||
this.readMBDN();
|
||||
} else {
|
||||
if (DEBUG) this.context.debug("MDN: MDN service is not available");
|
||||
|
||||
if (ICCUtilsHelper.isCphsServiceAvailable("MBN")) {
|
||||
// read CPHS_MBN in advance if MBDN is not available.
|
||||
this.readCphsMBN();
|
||||
} else {
|
||||
if (DEBUG) this.context.debug("CPHS_MBN: CPHS_MBN service is not available");
|
||||
}
|
||||
}
|
||||
|
||||
if (ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
|
||||
@ -13504,6 +13530,15 @@ SimRecordHelperObject.prototype = {
|
||||
let RIL = this.context.RIL;
|
||||
let contact =
|
||||
this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
|
||||
if ((!contact ||
|
||||
((!contact.alphaId || contact.alphaId == "") &&
|
||||
(!contact.number || contact.number == ""))) &&
|
||||
this.context.ICCUtilsHelper.isCphsServiceAvailable("MBN")) {
|
||||
// read CPHS_MBN in advance if MBDN is invalid or empty.
|
||||
this.readCphsMBN();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contact ||
|
||||
(RIL.iccInfoPrivate.mbdn !== undefined &&
|
||||
RIL.iccInfoPrivate.mbdn === contact.number)) {
|
||||
@ -14081,6 +14116,117 @@ SimRecordHelperObject.prototype = {
|
||||
callback: callback.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Read CPHS Phase & Service Table from CPHS Info.
|
||||
*
|
||||
* @See B.3.1.1 CPHS Information in CPHS Phase 2.
|
||||
*
|
||||
* @param onsuccess Callback to be called when success.
|
||||
* @param onerror Callback to be called when error.
|
||||
*/
|
||||
readCphsInfo: function(onsuccess, onerror) {
|
||||
function callback() {
|
||||
try {
|
||||
let Buf = this.context.Buf;
|
||||
let RIL = this.context.RIL;
|
||||
|
||||
let strLen = Buf.readInt32();
|
||||
// Each octet is encoded into two chars.
|
||||
let octetLen = strLen / 2;
|
||||
let cphsInfo = this.context.GsmPDUHelper.readHexOctetArray(octetLen);
|
||||
Buf.readStringDelimiter(strLen);
|
||||
if (DEBUG) {
|
||||
let str = "";
|
||||
for (let i = 0; i < cphsInfo.length; i++) {
|
||||
str += cphsInfo[i] + ", ";
|
||||
}
|
||||
this.context.debug("CPHS INFO: " + str);
|
||||
}
|
||||
|
||||
/**
|
||||
* CPHS INFORMATION
|
||||
*
|
||||
* Byte 1: CPHS Phase
|
||||
* 01 phase 1
|
||||
* 02 phase 2
|
||||
* etc.
|
||||
*
|
||||
* Byte 2: CPHS Service Table
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
* | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
* | ONSF | MBN | SST | CSP |
|
||||
* | Phase 2 | ALL | Phase 1 | All |
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
*
|
||||
* Byte 3: CPHS Service Table continued
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
* | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
* | RFU | RFU | RFU | INFO_NUM|
|
||||
* | | | | Phase 2 |
|
||||
* +----+----+----+----+----+----+----+----+
|
||||
*/
|
||||
let cphsPhase = cphsInfo[0];
|
||||
if (cphsPhase == 1) {
|
||||
// Clear 'Phase 2 only' services.
|
||||
cphsInfo[1] &= 0x3F;
|
||||
// We don't know whether Byte 3 is available in CPHS phase 1 or not.
|
||||
// Add boundary check before accessing it.
|
||||
if (cphsInfo.length > 2) {
|
||||
cphsInfo[2] = 0x00;
|
||||
}
|
||||
} else if (cphsPhase == 2) {
|
||||
// Clear 'Phase 1 only' services.
|
||||
cphsInfo[1] &= 0xF3;
|
||||
} else {
|
||||
throw new Error("Unknown CPHS phase: " + cphsPhase);
|
||||
}
|
||||
|
||||
RIL.iccInfoPrivate.cphsSt = cphsInfo.subarray(1);
|
||||
onsuccess();
|
||||
} catch(e) {
|
||||
onerror(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
this.context.ICCIOHelper.loadTransparentEF({
|
||||
fileId: ICC_EF_CPHS_INFO,
|
||||
callback: callback.bind(this),
|
||||
onerror: onerror
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Read CPHS MBN. (Mailbox Numbers)
|
||||
*
|
||||
* @See B.4.2.2 Voice Message Retrieval and Indicator Clearing
|
||||
*/
|
||||
readCphsMBN: function() {
|
||||
function callback(options) {
|
||||
let RIL = this.context.RIL;
|
||||
let contact =
|
||||
this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
|
||||
if (!contact ||
|
||||
(RIL.iccInfoPrivate.mbdn !== undefined &&
|
||||
RIL.iccInfoPrivate.mbdn === contact.number)) {
|
||||
return;
|
||||
}
|
||||
RIL.iccInfoPrivate.mbdn = contact.number;
|
||||
if (DEBUG) {
|
||||
this.context.debug("CPHS_MDN, alphaId=" + contact.alphaId +
|
||||
" number=" + contact.number);
|
||||
}
|
||||
contact.rilMessageType = "iccmbdn";
|
||||
RIL.sendChromeMessage(contact);
|
||||
}
|
||||
|
||||
this.context.ICCIOHelper.loadLinearFixedEF({
|
||||
fileId: ICC_EF_CPHS_MBN,
|
||||
callback: callback.bind(this)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function RuimRecordHelperObject(aContext) {
|
||||
@ -14662,8 +14808,8 @@ ICCUtilsHelperObject.prototype = {
|
||||
*
|
||||
* b1 = 0, service not allocated.
|
||||
* 1, service allocated.
|
||||
* b2 = 0, service not activatd.
|
||||
* 1, service allocated.
|
||||
* b2 = 0, service not activated.
|
||||
* 1, service activated.
|
||||
*
|
||||
* @see 3GPP TS 51.011 10.3.7.
|
||||
*/
|
||||
@ -14689,8 +14835,6 @@ ICCUtilsHelperObject.prototype = {
|
||||
*
|
||||
* b1 = 0, service not avaiable.
|
||||
* 1, service available.
|
||||
* b2 = 0, service not avaiable.
|
||||
* 1, service available.
|
||||
*
|
||||
* @see 3GPP TS 31.102 4.2.8.
|
||||
*/
|
||||
@ -14708,6 +14852,49 @@ ICCUtilsHelperObject.prototype = {
|
||||
((serviceTable[index] & bitmask) !== 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get whether specificed CPHS service is available.
|
||||
*
|
||||
* @param geckoService
|
||||
* Service name like "MDN", etc.
|
||||
*
|
||||
* @return true if the service is enabled, false otherwise.
|
||||
*/
|
||||
isCphsServiceAvailable: function(geckoService) {
|
||||
let RIL = this.context.RIL;
|
||||
let serviceTable = RIL.iccInfoPrivate.cphsSt;
|
||||
|
||||
if (!(serviceTable instanceof Uint8Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service id is valid in 1..N, and 2 bits are used to code each service.
|
||||
*
|
||||
* +----+-- --+----+----+
|
||||
* | b8 | ... | b2 | b1 |
|
||||
* +----+-- --+----+----+
|
||||
*
|
||||
* b1 = 0, service not allocated.
|
||||
* 1, service allocated.
|
||||
* b2 = 0, service not activated.
|
||||
* 1, service activated.
|
||||
*
|
||||
* @See B.3.1.1 CPHS Information in CPHS Phase 2.
|
||||
*/
|
||||
let cphsService = GECKO_ICC_SERVICES.cphs[geckoService];
|
||||
|
||||
if (!cphsService) {
|
||||
return false;
|
||||
}
|
||||
cphsService -= 1;
|
||||
let index = Math.floor(cphsService / 4);
|
||||
let bitmask = 2 << ((cphsService % 4) << 1);
|
||||
|
||||
return (index < serviceTable.length) &&
|
||||
((serviceTable[index] & bitmask) !== 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the string is of GSM default 7-bit coded alphabets with bit 8
|
||||
* set to 0.
|
||||
|
@ -296,3 +296,31 @@ add_test(function test_get_network_name_from_icc() {
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify ICCUtilsHelper.isCphsServiceAvailable.
|
||||
*/
|
||||
add_test(function test_is_cphs_service_available() {
|
||||
let worker = newUint8Worker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let ICCUtilsHelper = context.ICCUtilsHelper;
|
||||
let RIL = context.RIL;
|
||||
RIL.iccInfoPrivate.cphsSt = Uint8Array(2);
|
||||
|
||||
function test_table(cphsSt, geckoService) {
|
||||
RIL.iccInfoPrivate.cphsSt.set(cphsSt);
|
||||
|
||||
for (let service in GECKO_ICC_SERVICES.cphs) {
|
||||
do_check_eq(ICCUtilsHelper.isCphsServiceAvailable(service),
|
||||
geckoService == service);
|
||||
}
|
||||
}
|
||||
|
||||
test_table([0x03, 0x00], "CSP");
|
||||
test_table([0x0C, 0x00], "SST");
|
||||
test_table([0x30, 0x00], "MBN");
|
||||
test_table([0xC0, 0x00], "ONSF");
|
||||
test_table([0x00, 0x03], "INFO_NUM");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -160,14 +160,14 @@ add_test(function test_reading_optional_efs() {
|
||||
/**
|
||||
* Verify fetchSimRecords.
|
||||
*/
|
||||
add_test(function test_fetch_sim_recodes() {
|
||||
add_test(function test_fetch_sim_records() {
|
||||
let worker = newWorker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let RIL = context.RIL;
|
||||
let iccRecord = context.ICCRecordHelper;
|
||||
let simRecord = context.SimRecordHelper;
|
||||
|
||||
function testFetchSimRecordes(expectCalled) {
|
||||
function testFetchSimRecordes(expectCalled, expectCphsSuccess) {
|
||||
let ifCalled = [];
|
||||
|
||||
RIL.getIMSI = function() {
|
||||
@ -178,6 +178,15 @@ add_test(function test_fetch_sim_recodes() {
|
||||
ifCalled.push("readAD");
|
||||
};
|
||||
|
||||
simRecord.readCphsInfo = function(onsuccess, onerror) {
|
||||
ifCalled.push("readCphsInfo");
|
||||
if (expectCphsSuccess) {
|
||||
onsuccess();
|
||||
} else {
|
||||
onerror();
|
||||
}
|
||||
};
|
||||
|
||||
simRecord.readSST = function() {
|
||||
ifCalled.push("readSST");
|
||||
};
|
||||
@ -192,8 +201,9 @@ add_test(function test_fetch_sim_recodes() {
|
||||
}
|
||||
}
|
||||
|
||||
let expectCalled = ["getIMSI", "readAD", "readSST"];
|
||||
testFetchSimRecordes(expectCalled);
|
||||
let expectCalled = ["getIMSI", "readAD", "readCphsInfo", "readSST"];
|
||||
testFetchSimRecordes(expectCalled, true);
|
||||
testFetchSimRecordes(expectCalled, false);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -782,7 +792,7 @@ add_test(function test_reading_img_length_error() {
|
||||
let ril = context.RIL;
|
||||
let buf = context.Buf;
|
||||
let io = context.ICCIOHelper;
|
||||
|
||||
|
||||
let test_data = [
|
||||
{/* Offset length not enough, should be 4. */
|
||||
img: [0x01, 0x05, 0x05, 0x11, 0x4f, 0x00, 0x00, 0x04, 0x00, 0x06],
|
||||
@ -1292,3 +1302,204 @@ add_test(function test_reading_img_color() {
|
||||
}
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify SimRecordHelper.readCphsInfo
|
||||
*/
|
||||
add_test(function test_read_cphs_info() {
|
||||
let worker = newUint8Worker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let RIL = context.RIL;
|
||||
let pduHelper = context.GsmPDUHelper;
|
||||
let recordHelper = context.SimRecordHelper;
|
||||
let buf = context.Buf;
|
||||
let io = context.ICCIOHelper;
|
||||
let cphsPDU = Uint8Array(3);
|
||||
|
||||
io.loadTransparentEF = function(options) {
|
||||
if (cphsPDU) {
|
||||
// Write data size
|
||||
buf.writeInt32(cphsPDU.length * 2);
|
||||
|
||||
// Write CPHS INFO
|
||||
for (let i = 0; i < cphsPDU.length; i++) {
|
||||
pduHelper.writeHexOctet(cphsPDU[i]);
|
||||
}
|
||||
|
||||
// Write string delimiter
|
||||
buf.writeStringDelimiter(cphsPDU.length * 2);
|
||||
|
||||
if (options.callback) {
|
||||
options.callback(options);
|
||||
}
|
||||
} else {
|
||||
do_print("cphsPDU[] is not set.");
|
||||
}
|
||||
};
|
||||
|
||||
function do_test(cphsInfo, cphsSt) {
|
||||
let onsuccess = false;
|
||||
let onerror = false;
|
||||
|
||||
delete RIL.iccInfoPrivate.cphsSt;
|
||||
cphsPDU.set(cphsInfo);
|
||||
recordHelper.readCphsInfo(() => { onsuccess = true; },
|
||||
() => { onerror = true; });
|
||||
|
||||
do_check_true((cphsSt) ? onsuccess : onerror);
|
||||
do_check_false((cphsSt) ? onerror : onsuccess);
|
||||
if (cphsSt) {
|
||||
do_check_eq(RIL.iccInfoPrivate.cphsSt.length, cphsSt.length);
|
||||
for (let i = 0; i < cphsSt.length; i++) {
|
||||
do_check_eq(RIL.iccInfoPrivate.cphsSt[i], cphsSt[i]);
|
||||
}
|
||||
} else {
|
||||
do_check_eq(RIL.iccInfoPrivate.cphsSt, cphsSt);
|
||||
}
|
||||
}
|
||||
|
||||
do_test([
|
||||
0x01, // Phase 1
|
||||
0xFF, // All available & activated
|
||||
0x03 // All available & activated
|
||||
],
|
||||
[
|
||||
0x3F, // All services except ONSF(bit 8-7) are available and activated.
|
||||
0x00 // INFO_NUM shall not be available & activated.
|
||||
]);
|
||||
|
||||
do_test([
|
||||
0x02, // Phase 2
|
||||
0xFF, // All available & activated
|
||||
0x03 // All available & activated
|
||||
],
|
||||
[
|
||||
0xF3, // All services except ONSF are available and activated.
|
||||
0x03 // INFO_NUM shall not be available & activated.
|
||||
]);
|
||||
|
||||
do_test([
|
||||
0x03, // Phase 3
|
||||
0xFF, // All available & activated
|
||||
0x03 // All available & activated
|
||||
],
|
||||
undefined); // RIL.iccInfoPrivate.cphsSt shall be remained as 'undefined'.
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify SimRecordHelper.readMBDN/SimRecordHelper.readCphsMBN
|
||||
*/
|
||||
add_test(function test_read_voicemail_number() {
|
||||
let worker = newUint8Worker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let RIL = context.RIL;
|
||||
let pduHelper = context.GsmPDUHelper;
|
||||
let recordHelper = context.SimRecordHelper;
|
||||
let buf = context.Buf;
|
||||
let io = context.ICCIOHelper;
|
||||
let postedMessage;
|
||||
|
||||
worker.postMessage = function(message) {
|
||||
postedMessage = message;
|
||||
};
|
||||
|
||||
io.loadLinearFixedEF = function(options) {
|
||||
let mbnData = [
|
||||
0x56, 0x6F, 0x69, 0x63, 0x65, 0x6D, 0x61, 0x69,
|
||||
0x6C, 0xFF, // Alpha Identifier: Voicemail
|
||||
0x03, // Length of BCD number: 3
|
||||
0x80, // TOA: Unknown
|
||||
0x11, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, // Dialing Number: 111
|
||||
0xFF, // Capability/Configuration Record Identifier
|
||||
0xFF // Extension Record Identifier
|
||||
];
|
||||
|
||||
// Write data size
|
||||
buf.writeInt32(mbnData.length * 2);
|
||||
|
||||
// Write MBN
|
||||
for (let i = 0; i < mbnData.length; i++) {
|
||||
pduHelper.writeHexOctet(mbnData[i]);
|
||||
}
|
||||
|
||||
// Write string delimiter
|
||||
buf.writeStringDelimiter(mbnData.length * 2);
|
||||
|
||||
options.recordSize = mbnData.length;
|
||||
if (options.callback) {
|
||||
options.callback(options);
|
||||
}
|
||||
};
|
||||
|
||||
function do_test(funcName, msgCount) {
|
||||
postedMessage = null;
|
||||
delete RIL.iccInfoPrivate.mbdn;
|
||||
recordHelper[funcName]();
|
||||
|
||||
do_check_eq("iccmbdn", postedMessage.rilMessageType);
|
||||
do_check_eq("Voicemail", postedMessage.alphaId);
|
||||
do_check_eq("111", postedMessage.number);
|
||||
}
|
||||
|
||||
do_test("readMBDN");
|
||||
do_test("readCphsMBN");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify the recovery from SimRecordHelper.readCphsMBN() if MBDN is not valid
|
||||
* or is empty after SimRecordHelper.readMBDN().
|
||||
*/
|
||||
add_test(function test_read_mbdn_recovered_from_cphs_mbn() {
|
||||
let worker = newUint8Worker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let RIL = context.RIL;
|
||||
let pduHelper = context.GsmPDUHelper;
|
||||
let recordHelper = context.SimRecordHelper;
|
||||
let iccUtilsHelper = context.ICCUtilsHelper;
|
||||
let buf = context.Buf;
|
||||
let io = context.ICCIOHelper;
|
||||
|
||||
io.loadLinearFixedEF = function(options) {
|
||||
let mbnData = [
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
];
|
||||
|
||||
// Write data size
|
||||
buf.writeInt32(mbnData.length * 2);
|
||||
|
||||
// Write MBN
|
||||
for (let i = 0; i < mbnData.length; i++) {
|
||||
pduHelper.writeHexOctet(mbnData[i]);
|
||||
}
|
||||
|
||||
// Write string delimiter
|
||||
buf.writeStringDelimiter(mbnData.length * 2);
|
||||
|
||||
options.recordSize = mbnData.length;
|
||||
if (options.callback) {
|
||||
options.callback(options);
|
||||
}
|
||||
};
|
||||
|
||||
iccUtilsHelper.isCphsServiceAvailable = function(geckoService) {
|
||||
return geckoService == "MBN";
|
||||
};
|
||||
|
||||
let isRecovered = false;
|
||||
recordHelper.readCphsMBN = function(onComplete) {
|
||||
isRecovered = true;
|
||||
};
|
||||
|
||||
recordHelper.readMBDN();
|
||||
|
||||
do_check_eq(RIL.iccInfoPrivate.mbdn, undefined);
|
||||
do_check_true(isRecovered);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -1233,6 +1233,24 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "TreeSelection", xbl: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"TreeWalker",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVChannel", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVCurrentChannelChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVCurrentSourceChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVEITBroadcastedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVManager", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVProgram", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVScanningStateChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVSource", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "TVTuner", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "UDPMessageEvent", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
196
dom/tv/FakeTVService.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "mozilla/dom/TVServiceRunnables.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "FakeTVService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(FakeTVService, nsITVService)
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetSourceListener(nsITVSourceListener** aSourceListener)
|
||||
{
|
||||
*aSourceListener = mSourceListener;
|
||||
NS_ADDREF(*aSourceListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetSourceListener(nsITVSourceListener* aSourceListener)
|
||||
{
|
||||
mSourceListener = aSourceListener;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetTuners(nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> tunerDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!tunerDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, tunerDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetSource(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::StartScanningChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::StopScanningChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::ClearScannedChannelsCache()
|
||||
{
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetChannel(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
const nsAString& aChannelNumber,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!channelDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, channelDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!channelDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, channelDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetPrograms(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
const nsAString& aChannelNumber,
|
||||
uint64_t startTime,
|
||||
uint64_t endTime,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> programDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!programDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, programDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetOverlayId(const nsAString& aTunerId,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> overlayIds = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!overlayIds) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, overlayIds);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
40
dom/tv/FakeTVService.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_dom_FakeTVService_h
|
||||
#define mozilla_dom_FakeTVService_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsITVService.h"
|
||||
|
||||
#define FAKE_TV_SERVICE_CONTRACTID \
|
||||
"@mozilla.org/tv/faketvservice;1"
|
||||
#define FAKE_TV_SERVICE_CID \
|
||||
{ 0x60fb3c53, 0x017f, 0x4340, { 0x91, 0x1b, 0xd5, 0x5c, 0x31, 0x28, 0x88, 0xb6 } }
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FakeTVService MOZ_FINAL : public nsITVService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITVSERVICE
|
||||
|
||||
FakeTVService() {}
|
||||
|
||||
// TODO More members might be added in follow-up patches to help testing.
|
||||
|
||||
private:
|
||||
~FakeTVService() {}
|
||||
|
||||
nsCOMPtr<nsITVSourceListener> mSourceListener;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FakeTVService_h
|
213
dom/tv/TVChannel.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/TVServiceCallbacks.h"
|
||||
#include "mozilla/dom/TVServiceFactory.h"
|
||||
#include "mozilla/dom/TVSource.h"
|
||||
#include "mozilla/dom/TVTuner.h"
|
||||
#include "mozilla/dom/TVUtils.h"
|
||||
#include "nsITVService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "TVChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(TVChannel, DOMEventTargetHelper,
|
||||
mTVService, mSource)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(TVChannel, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(TVChannel, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVChannel)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
TVChannel::TVChannel(nsPIDOMWindow* aWindow,
|
||||
TVSource* aSource)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mSource(aSource)
|
||||
{
|
||||
MOZ_ASSERT(mSource);
|
||||
}
|
||||
|
||||
TVChannel::~TVChannel()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<TVChannel>
|
||||
TVChannel::Create(nsPIDOMWindow* aWindow,
|
||||
TVSource* aSource,
|
||||
nsITVChannelData* aData)
|
||||
{
|
||||
nsRefPtr<TVChannel> channel = new TVChannel(aWindow, aSource);
|
||||
return (channel->Init(aData)) ? channel.forget() : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
TVChannel::Init(nsITVChannelData* aData)
|
||||
{
|
||||
NS_ENSURE_TRUE(aData, false);
|
||||
|
||||
nsString channelType;
|
||||
aData->GetType(channelType);
|
||||
mType = ToTVChannelType(channelType);
|
||||
if (NS_WARN_IF(mType == TVChannelType::EndGuard_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aData->GetNetworkId(mNetworkId);
|
||||
aData->GetTransportStreamId(mTransportStreamId);
|
||||
aData->GetServiceId(mServiceId);
|
||||
aData->GetName(mName);
|
||||
aData->GetNumber(mNumber);
|
||||
aData->GetIsEmergency(&mIsEmergency);
|
||||
aData->GetIsFree(&mIsFree);
|
||||
|
||||
mTVService = TVServiceFactory::AutoCreateTVService();
|
||||
NS_ENSURE_TRUE(mTVService, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
TVChannel::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return TVChannelBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TVChannel::DispatchTVEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
return DispatchTrustedEvent(aEvent);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
TVChannel::GetPrograms(const TVGetProgramsOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TVTuner> tuner = mSource->Tuner();
|
||||
nsString tunerId;
|
||||
tuner->GetId(tunerId);
|
||||
|
||||
uint64_t startTime = aOptions.mStartTime.WasPassed() ?
|
||||
aOptions.mStartTime.Value() :
|
||||
PR_Now();
|
||||
uint64_t endTime = aOptions.mDuration.WasPassed() ?
|
||||
(startTime + aOptions.mDuration.Value()) :
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
nsCOMPtr<nsITVServiceCallback> callback =
|
||||
new TVServiceProgramGetterCallback(this, promise, false);
|
||||
nsresult rv =
|
||||
mTVService->GetPrograms(tunerId,
|
||||
ToTVSourceTypeStr(mSource->Type()),
|
||||
mNumber,
|
||||
startTime,
|
||||
endTime,
|
||||
callback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
TVChannel::GetNetworkId(nsAString& aNetworkId) const
|
||||
{
|
||||
aNetworkId = mNetworkId;
|
||||
}
|
||||
|
||||
void
|
||||
TVChannel::GetTransportStreamId(nsAString& aTransportStreamId) const
|
||||
{
|
||||
aTransportStreamId = mTransportStreamId;
|
||||
}
|
||||
|
||||
void
|
||||
TVChannel::GetServiceId(nsAString& aServiceId) const
|
||||
{
|
||||
aServiceId = mServiceId;
|
||||
}
|
||||
|
||||
already_AddRefed<TVSource>
|
||||
TVChannel::Source() const
|
||||
{
|
||||
nsRefPtr<TVSource> source = mSource;
|
||||
return source.forget();
|
||||
}
|
||||
|
||||
TVChannelType
|
||||
TVChannel::Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void
|
||||
TVChannel::GetName(nsAString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
|
||||
void
|
||||
TVChannel::GetNumber(nsAString& aNumber) const
|
||||
{
|
||||
aNumber = mNumber;
|
||||
}
|
||||
|
||||
bool
|
||||
TVChannel::IsEmergency() const
|
||||
{
|
||||
return mIsEmergency;
|
||||
}
|
||||
|
||||
bool
|
||||
TVChannel::IsFree() const
|
||||
{
|
||||
return mIsFree;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
TVChannel::GetCurrentProgram(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TVTuner> tuner = mSource->Tuner();
|
||||
nsString tunerId;
|
||||
tuner->GetId(tunerId);
|
||||
|
||||
// Get only one program from now on.
|
||||
nsCOMPtr<nsITVServiceCallback> callback =
|
||||
new TVServiceProgramGetterCallback(this, promise, true);
|
||||
nsresult rv =
|
||||
mTVService->GetPrograms(tunerId,
|
||||
ToTVSourceTypeStr(mSource->Type()),
|
||||
mNumber,
|
||||
PR_Now(),
|
||||
std::numeric_limits<uint64_t>::max(),
|
||||
callback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
88
dom/tv/TVChannel.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_dom_TVChannel_h
|
||||
#define mozilla_dom_TVChannel_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
// Include TVChannelBinding.h since enum TVChannelType can't be forward declared.
|
||||
#include "mozilla/dom/TVChannelBinding.h"
|
||||
|
||||
class nsITVChannelData;
|
||||
class nsITVService;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class TVProgram;
|
||||
class TVSource;
|
||||
|
||||
class TVChannel MOZ_FINAL : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVChannel, DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<TVChannel> Create(nsPIDOMWindow* aWindow,
|
||||
TVSource* aSource,
|
||||
nsITVChannelData* aData);
|
||||
|
||||
// WebIDL (internal functions)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
|
||||
|
||||
nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
// WebIDL (public APIs)
|
||||
|
||||
already_AddRefed<Promise> GetPrograms(const TVGetProgramsOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> GetCurrentProgram(ErrorResult& aRv);
|
||||
|
||||
void GetNetworkId(nsAString& aNetworkId) const;
|
||||
|
||||
void GetTransportStreamId(nsAString& aTransportStreamId) const;
|
||||
|
||||
void GetServiceId(nsAString& aServiceId) const;
|
||||
|
||||
already_AddRefed<TVSource> Source() const;
|
||||
|
||||
TVChannelType Type() const;
|
||||
|
||||
void GetName(nsAString& aName) const;
|
||||
|
||||
void GetNumber(nsAString& aNumber) const;
|
||||
|
||||
bool IsEmergency() const;
|
||||
|
||||
bool IsFree() const;
|
||||
|
||||
private:
|
||||
TVChannel(nsPIDOMWindow* aWindow,
|
||||
TVSource* aSource);
|
||||
|
||||
~TVChannel();
|
||||
|
||||
bool Init(nsITVChannelData* aData);
|
||||
|
||||
nsCOMPtr<nsITVService> mTVService;
|
||||
nsRefPtr<TVSource> mSource;
|
||||
nsString mNetworkId;
|
||||
nsString mTransportStreamId;
|
||||
nsString mServiceId;
|
||||
TVChannelType mType;
|
||||
nsString mNumber;
|
||||
nsString mName;
|
||||
bool mIsEmergency;
|
||||
bool mIsFree;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TVChannel_h
|
105
dom/tv/TVListeners.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "mozilla/dom/TVSource.h"
|
||||
#include "mozilla/dom/TVTuner.h"
|
||||
#include "mozilla/dom/TVUtils.h"
|
||||
#include "TVListeners.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(TVSourceListener, nsITVSourceListener)
|
||||
|
||||
void
|
||||
TVSourceListener::RegisterSource(TVSource* aSource)
|
||||
{
|
||||
nsString tunerId;
|
||||
nsRefPtr<TVTuner> tuner = aSource->Tuner();
|
||||
tuner->GetId(tunerId);
|
||||
|
||||
nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
|
||||
if (!mSources.Get(tunerId, &tunerSources)) {
|
||||
tunerSources = new nsRefPtrHashtable<nsStringHashKey, TVSource>();
|
||||
mSources.Put(tunerId, tunerSources);
|
||||
}
|
||||
|
||||
nsString sourceType = ToTVSourceTypeStr(aSource->Type());
|
||||
tunerSources->Put(sourceType, aSource);
|
||||
}
|
||||
|
||||
void
|
||||
TVSourceListener::UnregisterSource(TVSource* aSource)
|
||||
{
|
||||
nsString tunerId;
|
||||
nsRefPtr<TVTuner> tuner = aSource->Tuner();
|
||||
tuner->GetId(tunerId);
|
||||
|
||||
nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
|
||||
if (!mSources.Get(tunerId, &tunerSources)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsString sourceType = ToTVSourceTypeStr(aSource->Type());
|
||||
tunerSources->Remove(sourceType);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
TVSourceListener::NotifyChannelScanned(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVChannelData* aChannelData)
|
||||
{
|
||||
nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
|
||||
source->NotifyChannelScanned(aChannelData);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
TVSourceListener::NotifyChannelScanComplete(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType)
|
||||
{
|
||||
nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
|
||||
source->NotifyChannelScanComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
TVSourceListener::NotifyChannelScanStopped(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType)
|
||||
{
|
||||
nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
|
||||
source->NotifyChannelScanStopped();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
TVSourceListener::NotifyEITBroadcasted(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVChannelData* aChannelData,
|
||||
nsITVProgramData** aProgramDataList,
|
||||
const uint32_t aCount)
|
||||
{
|
||||
nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
|
||||
source->NotifyEITBroadcasted(aChannelData, aProgramDataList, aCount);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TVSource>
|
||||
TVSourceListener::GetSource(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType)
|
||||
{
|
||||
nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
|
||||
if (!mSources.Get(aTunerId, &tunerSources)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TVSource> source;
|
||||
tunerSources->Get(aSourceType, getter_AddRefs(source));
|
||||
return source.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|