Merge m-c to b2g-inbound. a=merge
@ -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"
|
||||
}
|
||||
]
|
||||
|
@ -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);
|
||||
|
@ -1,5 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
addon1.xpi
|
||||
@ -16,6 +15,7 @@ support-files =
|
||||
code_blackboxing_two.js
|
||||
code_breakpoints-break-on-last-line-of-script-on-reload.js
|
||||
code_breakpoints-other-tabs.js
|
||||
code_frame-script.js
|
||||
code_function-search-01.js
|
||||
code_function-search-02.js
|
||||
code_function-search-03.js
|
||||
@ -98,233 +98,452 @@ support-files =
|
||||
testactors.js
|
||||
|
||||
[browser_dbg_aaa_run_first_leaktest.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addonactor.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addon-sources.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addon-modules.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addon-modules-unpacked.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addon-panels.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_addon-console.js]
|
||||
skip-if = os == 'win' # bug 1005274
|
||||
skip-if = e10s || os == 'win' # bug 1005274
|
||||
[browser_dbg_auto-pretty-print-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_auto-pretty-print-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_bfcache.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_blackboxing-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-03.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-04.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-05.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-06.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breadcrumbs-access.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-08.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_break-on-dom-event-01.js]
|
||||
skip-if = os == "mac" || e10s # Bug 895426
|
||||
skip-if = e10s || os == "mac" || e10s # Bug 895426
|
||||
[browser_dbg_break-on-dom-event-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-actual-location.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-actual-location2.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-button-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-button-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-contextmenu-add.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-contextmenu.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-disabled-reload.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-editor.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-highlight.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-new-script.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-other-tabs.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-pane.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_breakpoints-reload.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_chrome-create.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_chrome-debugging.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_clean-exit-window.js]
|
||||
skip-if = true # Bug 933950 (leaky test)
|
||||
[browser_dbg_clean-exit.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_closure-inspection.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_cmd-blackbox.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_cmd-break.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_cmd-dbg.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_conditional-breakpoints-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_conditional-breakpoints-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_conditional-breakpoints-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_conditional-breakpoints-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_server-conditional-bp-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_server-conditional-bp-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_server-conditional-bp-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_server-conditional-bp-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_controller-evaluate-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_controller-evaluate-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_debugger-statement.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_editor-contextmenu.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_editor-mode.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_event-listeners-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_event-listeners-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_event-listeners-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_file-reload.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_function-display-name.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_global-method-override.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_globalactor.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_hit-counts-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_hit-counts-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_host-layout.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_iframes.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_instruments-pane-collapse.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_interrupts.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_listaddons.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_listtabs-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_listtabs-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_listtabs-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_location-changes-01-simple.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_location-changes-02-blank.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_location-changes-03-new.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_location-changes-04-breakpoint.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_multiple-windows.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_navigation.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_no-page-sources.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_on-pause-highlight.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_on-pause-raise.js]
|
||||
skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
|
||||
skip-if = e10s || os == "linux" || e10s # Bug 888811 & bug 891176
|
||||
[browser_dbg_optimized-out-vars.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_panel-size.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-08.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-09.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_parser-10.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pause-exceptions-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pause-exceptions-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pause-resume.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pause-warning.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_paused-keybindings.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-08.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-09.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-10.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-11.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-12.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-13.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_pretty-print-on-paused.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_progress-listener-bug.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_reload-preferred-script-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_reload-preferred-script-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_reload-preferred-script-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_reload-same-script.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_scripts-switching-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_scripts-switching-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_scripts-switching-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-autofill-identifier.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-basic-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-basic-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-basic-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-basic-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-global-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-popup-jank.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-sources-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-sources-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-sources-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_search-symbols.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_searchbox-help-popup-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_searchbox-help-popup-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_searchbox-parse.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_source-maps-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_source-maps-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_source-maps-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_source-maps-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_sources-cache.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_sources-labels.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_sources-sorting.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_split-console-paused-reload.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_stack-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_step-out.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tabactor-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tabactor-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_terminate-on-tab-close.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_tracing-08.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-accessibility.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-data.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-edit-cancel.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-edit-click.js]
|
||||
skip-if = (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
|
||||
skip-if = e10s || (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
|
||||
[browser_dbg_variables-view-edit-getset-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-edit-getset-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-edit-value.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-edit-watch.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-pref.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-filter-searchbox.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-frame-parameters-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-frame-parameters-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-frame-parameters-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-frame-with.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-frozen-sealed-nonext.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
skip-if = e10s || buildapp == 'mulet'
|
||||
[browser_dbg_variables-view-hide-non-enums.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-large-array-buffer.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-override-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-override-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-04.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-05.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-06.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-07.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-08.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-09.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-10.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-11.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-12.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-13.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-14.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-15.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-popup-16.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-reexpand-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-reexpand-02.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-reexpand-03.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_variables-view-webidl.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_watch-expressions-01.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_watch-expressions-02.js]
|
||||
skip-if = e10s
|
||||
|
@ -7,12 +7,11 @@
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
|
||||
@ -48,7 +47,6 @@ function testBlackBoxReload() {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
});
|
||||
|
@ -9,13 +9,12 @@
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
@ -44,15 +43,12 @@ function testBlackBoxStack() {
|
||||
"And one of them should be the combined black boxed frames.");
|
||||
});
|
||||
|
||||
// Spin the event loop before causing the debuggee to pause, to allow
|
||||
// this function to return first.
|
||||
executeSoon(() => gDebuggee.runTest());
|
||||
callInTab(gTab, "runTest");
|
||||
return finished;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
|
@ -9,13 +9,12 @@
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
@ -28,7 +27,7 @@ function test() {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
callInTab(gTab, "runTest");
|
||||
});
|
||||
}
|
||||
|
||||
@ -52,7 +51,6 @@ function testBlackBoxSource() {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
|
@ -9,13 +9,12 @@
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
@ -46,15 +45,12 @@ function testBlackBoxStack() {
|
||||
"And 'one', 'two', and 'three' should each have their own black boxed frame.");
|
||||
});
|
||||
|
||||
// Spin the event loop before causing the debuggee to pause, to allow
|
||||
// this function to return first.
|
||||
executeSoon(() => gDebuggee.one());
|
||||
callInTab(gTab, "one");
|
||||
return finished;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
|
@ -8,13 +8,12 @@
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gDeck;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gDeck = gDebugger.document.getElementById("editor-deck");
|
||||
@ -64,7 +63,6 @@ function getEditorBlackboxMessageButton() {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gDeck = null;
|
||||
|
@ -8,13 +8,12 @@
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gSources;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
@ -26,7 +25,7 @@ function test() {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
callInTab(gTab, "runTest");
|
||||
});
|
||||
}
|
||||
|
||||
@ -50,7 +49,6 @@ function testBlackBox() {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gSources = null;
|
||||
|
9
browser/devtools/debugger/test/code_frame-script.js
Normal file
@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
dump("Frame script loaded.\n");
|
||||
|
||||
addMessageListener("test:call", function (message) {
|
||||
dump("Calling function with name " + message.data + ".\n");
|
||||
|
||||
XPCNativeWrapper.unwrap(content)[message.data]();
|
||||
});
|
@ -28,6 +28,7 @@ let TargetFactory = devtools.TargetFactory;
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
|
||||
const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
|
||||
|
||||
gDevTools.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
@ -86,6 +87,9 @@ function addTab(aUrl, aWindow) {
|
||||
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
|
||||
let linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
info("Loading frame script with url " + FRAME_SCRIPT_URL + ".");
|
||||
linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
|
||||
|
||||
linkedBrowser.addEventListener("load", function onLoad() {
|
||||
linkedBrowser.removeEventListener("load", onLoad, true);
|
||||
info("Tab added and finished loading: " + aUrl);
|
||||
@ -936,4 +940,28 @@ function popPrefs() {
|
||||
let deferred = promise.defer();
|
||||
SpecialPowers.popPrefEnv(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessageToTab(tab, name, data, objects) {
|
||||
info("Sending message with name " + name + " to tab.");
|
||||
|
||||
tab.linkedBrowser.messageManager.sendAsyncMessage(name, data, objects);
|
||||
}
|
||||
|
||||
function waitForMessageFromTab(tab, name) {
|
||||
info("Waiting for message with name " + name + " from tab.");
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
let messageManager = tab.linkedBrowser.messageManager;
|
||||
messageManager.addMessageListener(name, function listener(message) {
|
||||
messageManager.removeMessageListener(name, listener);
|
||||
resolve(message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function callInTab(tab, name) {
|
||||
info("Calling function with name " + name + " in tab.");
|
||||
|
||||
sendMessageToTab(tab, "test:call", name);
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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…
|
||||
|
@ -125,6 +125,14 @@ PluginContent.prototype = {
|
||||
pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
|
||||
pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);
|
||||
|
||||
// Convert this from nsIPluginTag so it can be serialized.
|
||||
let properties = ["name", "description", "filename", "version", "enabledState"];
|
||||
let pluginTagCopy = {};
|
||||
for (let prop of properties) {
|
||||
pluginTagCopy[prop] = pluginTag[prop];
|
||||
}
|
||||
pluginTag = pluginTagCopy;
|
||||
|
||||
permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
|
||||
fallbackType = pluginElement.defaultFallbackType;
|
||||
blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3114,6 +3127,15 @@ nsDocShell::NotifyAsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos)
|
||||
mScrollObservers.RemoveElement(ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Also notify child docshell
|
||||
for (uint32_t i = 0; i < mChildList.Length(); ++i) {
|
||||
nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
|
||||
if (kid) {
|
||||
nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
|
||||
docShell->NotifyAsyncPanZoomStarted(aScrollPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -3129,6 +3151,15 @@ nsDocShell::NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
|
||||
mScrollObservers.RemoveElement(ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Also notify child docshell
|
||||
for (uint32_t i = 0; i < mChildList.Length(); ++i) {
|
||||
nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
|
||||
if (kid) {
|
||||
nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
|
||||
docShell->NotifyAsyncPanZoomStopped(aScrollPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -10378,8 +10409,8 @@ nsDocShell::DoURILoad(nsIURI * aURI,
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
|
||||
if (httpChannelInternal) {
|
||||
if (aForceAllowCookies) {
|
||||
httpChannelInternal->SetForceAllowThirdPartyCookie(true);
|
||||
}
|
||||
httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
|
||||
}
|
||||
if (aFirstParty) {
|
||||
httpChannelInternal->SetDocumentURI(aURI);
|
||||
} else {
|
||||
|
@ -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>
|
||||
|
@ -1099,7 +1099,10 @@ Navigator::SendBeacon(const nsAString& aUrl,
|
||||
}
|
||||
bool isForeign = true;
|
||||
thirdPartyUtil->IsThirdPartyWindow(mWindow, uri, &isForeign);
|
||||
httpChannelInternal->SetForceAllowThirdPartyCookie(!isForeign);
|
||||
uint32_t thirdPartyFlags = isForeign ?
|
||||
0 :
|
||||
nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW;
|
||||
httpChannelInternal->SetThirdPartyFlags(thirdPartyFlags);
|
||||
|
||||
nsCString mimeType;
|
||||
if (!aData.IsNull()) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 sts=2 ts=8 et 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/. */
|
||||
@ -166,12 +168,17 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
|
||||
|
||||
nsresult rv;
|
||||
bool doForce = false;
|
||||
bool checkWindowChain = true;
|
||||
bool parentIsThird = false;
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
|
||||
do_QueryInterface(aChannel);
|
||||
if (httpChannelInternal) {
|
||||
rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
|
||||
uint32_t flags;
|
||||
rv = httpChannelInternal->GetThirdPartyFlags(&flags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
doForce = (flags & nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
|
||||
|
||||
// If aURI was not supplied, and we're forcing, then we're by definition
|
||||
// not foreign. If aURI was supplied, we still want to check whether it's
|
||||
// foreign with respect to the channel URI. (The forcing only applies to
|
||||
@ -180,6 +187,28 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_THIRD_PARTY) {
|
||||
// Check that the two PARENT_IS_{THIRD,SAME}_PARTY are mutually exclusive.
|
||||
MOZ_ASSERT(!(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY));
|
||||
|
||||
// If we're not forcing and we know that the window chain of the channel
|
||||
// is third party, then we know now that we're third party.
|
||||
if (!doForce) {
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
checkWindowChain = false;
|
||||
parentIsThird = true;
|
||||
} else {
|
||||
// In e10s, we can't check the parent chain in the parent, so we do so
|
||||
// in the child and send the result to the parent.
|
||||
// Note that we only check the window chain if neither
|
||||
// THIRD_PARTY_PARENT_IS_* flag is set.
|
||||
checkWindowChain = !(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY);
|
||||
parentIsThird = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the URI from the channel, and its base domain.
|
||||
@ -206,6 +235,12 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
|
||||
}
|
||||
}
|
||||
|
||||
// If we've already computed this in the child process, we're done.
|
||||
if (!checkWindowChain) {
|
||||
*aResult = parentIsThird;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Find the associated window and its parent window.
|
||||
nsCOMPtr<nsILoadContext> ctx;
|
||||
NS_QueryNotificationCallbacks(aChannel, ctx);
|
||||
|
@ -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',
|
||||
|
@ -606,11 +606,6 @@ void HTMLMediaElement::ShutdownDecoder()
|
||||
{
|
||||
RemoveMediaElementFromURITable();
|
||||
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
|
||||
// TODO: This should be handled by ChangeNetworkState() so we have only one
|
||||
// place to call StopProgress().
|
||||
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
|
||||
mDecoder->StopProgress();
|
||||
}
|
||||
mDecoder->Shutdown();
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
@ -1792,11 +1787,7 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef MOZ_EME
|
||||
if (ContainsRestrictedContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
OutputMediaStream* out = mOutputStreams.AppendElement();
|
||||
#ifdef DEBUG
|
||||
// Estimate hints based on the type of the media element
|
||||
@ -4001,21 +3992,10 @@ HTMLMediaElement::GetMediaKeys() const
|
||||
return mMediaKeys;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLMediaElement::ContainsRestrictedContent()
|
||||
{
|
||||
return GetMediaKeys() != nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (MozAudioCaptured()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
if (!global) {
|
||||
@ -4039,7 +4019,7 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
||||
mMediaKeys->Shutdown();
|
||||
mMediaKeys = nullptr;
|
||||
}
|
||||
|
||||
|
||||
mMediaKeys = aMediaKeys;
|
||||
if (mMediaKeys) {
|
||||
if (NS_FAILED(mMediaKeys->Bind(this))) {
|
||||
@ -4050,8 +4030,6 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
||||
if (mDecoder) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
}
|
||||
// Update the same-origin status.
|
||||
NotifyDecoderPrincipalChanged();
|
||||
}
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
|
@ -550,7 +550,6 @@ public:
|
||||
// in the URL bar of the browser window.
|
||||
already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
|
||||
|
||||
bool ContainsRestrictedContent();
|
||||
#endif // MOZ_EME
|
||||
|
||||
bool MozAutoplayEnabled() const
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "nsPresContext.h"
|
||||
#include "nsPresState.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsRuleData.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsTextEditorState.h"
|
||||
#include "nsIController.h"
|
||||
@ -407,6 +408,18 @@ void
|
||||
HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
nsRuleData* aData)
|
||||
{
|
||||
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
|
||||
// wrap=off
|
||||
nsCSSValue* whiteSpace = aData->ValueForWhiteSpace();
|
||||
if (whiteSpace->GetUnit() == eCSSUnit_Null) {
|
||||
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
|
||||
if (value && value->Type() == nsAttrValue::eString &&
|
||||
value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
|
||||
whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE, eCSSUnit_Enumerated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes, aData);
|
||||
nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
|
||||
}
|
||||
@ -431,7 +444,13 @@ HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
|
||||
NS_IMETHODIMP_(bool)
|
||||
HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
|
||||
{
|
||||
static const MappedAttributeEntry attributes[] {
|
||||
{ &nsGkAtoms::wrap },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const MappedAttributeEntry* const map[] = {
|
||||
attributes,
|
||||
sDivAlignAttributeMap,
|
||||
sCommonAttributeMap,
|
||||
};
|
||||
|
15
dom/html/reftests/82711-1-ref.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea rows="10" cols="25" wrap="off">
|
||||
0 1 2 3
|
||||
4 5
|
||||
|
||||
6 7 8
|
||||
9
|
||||
|
||||
|
||||
This is a long line that could wrap.
|
||||
</textarea>
|
||||
</body>
|
||||
</html>
|
15
dom/html/reftests/82711-1.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea rows="10" cols="25" style="white-space: pre">
|
||||
0 1 2 3
|
||||
4 5
|
||||
|
||||
6 7 8
|
||||
9
|
||||
|
||||
|
||||
This is a long line that could wrap.
|
||||
</textarea>
|
||||
</body>
|
||||
</html>
|
15
dom/html/reftests/82711-2-ref.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea rows="10" cols="25" wrap="off" style="white-space: normal">
|
||||
0 1 2 3
|
||||
4 5
|
||||
|
||||
6 7 8
|
||||
9
|
||||
|
||||
|
||||
This is a long line that could wrap.
|
||||
</textarea>
|
||||
</body>
|
||||
</html>
|
8
dom/html/reftests/82711-2.html
Normal file
@ -0,0 +1,8 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea rows="10" cols="25" style="white-space: normal">
|
||||
0 1 2 3 4 5 6 7 8 9 This is a long line that could wrap.
|
||||
</textarea>
|
||||
</body>
|
||||
</html>
|
@ -5,6 +5,9 @@ include toblob-todataurl/reftest.list
|
||||
skip-if(B2G) == 41464-1a.html 41464-1-ref.html
|
||||
skip-if(B2G) == 41464-1b.html 41464-1-ref.html
|
||||
== 52019-1.html 52019-1-ref.html
|
||||
== 82711-1.html 82711-1-ref.html
|
||||
== 82711-2.html 82711-2-ref.html
|
||||
!= 82711-1-ref.html 82711-2-ref.html
|
||||
!= 468263-1a.html about:blank
|
||||
!= 468263-1b.html about:blank
|
||||
!= 468263-1c.html about:blank
|
||||
|
@ -249,14 +249,34 @@ this.Keyboard = {
|
||||
},
|
||||
|
||||
forwardEvent: function keyboardForwardEvent(newEventName, msg) {
|
||||
this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
|
||||
.frameLoader.messageManager;
|
||||
let mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
|
||||
.frameLoader.messageManager;
|
||||
if (newEventName === 'Keyboard:FocusChange' &&
|
||||
msg.data.type === 'blur') {
|
||||
// A blur message can't be sent to the keyboard if the focus has
|
||||
// already taken away at first place.
|
||||
// This check is here to prevent problem caused by out-of-order
|
||||
// ipc messages from two processes.
|
||||
if (mm !== this.formMM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.sendToKeyboard(newEventName, msg.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
this.formMM = mm;
|
||||
|
||||
this.sendToKeyboard(newEventName, msg.data);
|
||||
return true;
|
||||
},
|
||||
|
||||
handleFocusChange: function keyboardHandleFocusChange(msg) {
|
||||
this.forwardEvent('Keyboard:FocusChange', msg);
|
||||
let isSent = this.forwardEvent('Keyboard:FocusChange', msg);
|
||||
|
||||
if (!isSent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chrome event, used also to render value selectors; that's why we need
|
||||
// the info about choices / min / max here as well...
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "mozilla/layers/PCompositorChild.h"
|
||||
#include "mozilla/layers/SharedBufferManagerChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
#if defined(XP_WIN)
|
||||
@ -932,6 +933,13 @@ ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* /* a
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::plugins::PPluginModuleParent*
|
||||
ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
{
|
||||
return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess);
|
||||
}
|
||||
|
||||
PContentBridgeChild*
|
||||
ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
|
@ -101,6 +101,10 @@ public:
|
||||
}
|
||||
nsRefPtr<ContentBridgeParent> mLastBridge;
|
||||
|
||||
PPluginModuleParent *
|
||||
AllocPPluginModuleParent(mozilla::ipc::Transport* transport,
|
||||
base::ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
||||
PContentBridgeParent*
|
||||
AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
|
||||
base::ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
@ -70,6 +70,7 @@
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "mozilla/layers/SharedBufferManagerParent.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
#include "mozilla/plugins/PluginBridge.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
@ -922,6 +923,20 @@ ContentParent::AnswerBridgeToChildProcess(const uint64_t& id)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId)
|
||||
{
|
||||
return mozilla::plugins::SetupBridge(aPluginId, this);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
{
|
||||
return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
}
|
||||
|
||||
/*static*/ TabParent*
|
||||
ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
||||
Element* aFrameElement,
|
||||
|
@ -148,6 +148,11 @@ public:
|
||||
bool* aIsForBrowser) MOZ_OVERRIDE;
|
||||
virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
|
||||
virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
@ -25,7 +25,7 @@ CrashReporterChild::GetCrashReporter()
|
||||
break;
|
||||
}
|
||||
case GeckoProcessType_Plugin: {
|
||||
PluginModuleChild* child = PluginModuleChild::current();
|
||||
PluginModuleChild* child = PluginModuleChild::GetChrome();
|
||||
reporters = &child->ManagedPCrashReporterChild();
|
||||
break;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ include protocol PImageBridge;
|
||||
include protocol PMemoryReportRequest;
|
||||
include protocol PMobileConnection;
|
||||
include protocol PNecko;
|
||||
include protocol PPluginModule;
|
||||
include protocol PPrinting;
|
||||
include protocol PScreenManager;
|
||||
include protocol PSharedBufferManager;
|
||||
@ -41,6 +42,7 @@ include JavaScriptTypes;
|
||||
include InputStreamParams;
|
||||
include PTabContext;
|
||||
include URIParams;
|
||||
include PluginTypes;
|
||||
include ProtocolTypes;
|
||||
|
||||
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
|
||||
@ -328,6 +330,8 @@ union MaybeFileDesc {
|
||||
|
||||
prio(normal upto high) intr protocol PContent
|
||||
{
|
||||
parent spawns PPluginModule;
|
||||
|
||||
parent opens PCompositor;
|
||||
parent opens PSharedBufferManager;
|
||||
parent opens PImageBridge;
|
||||
@ -532,6 +536,29 @@ parent:
|
||||
returns (uint64_t id, bool isForApp, bool isForBrowser);
|
||||
intr BridgeToChildProcess(uint64_t id);
|
||||
|
||||
/**
|
||||
* This call connects the content process to a plugin process. While this
|
||||
* call runs, a new PluginModuleParent will be created in the ContentChild
|
||||
* via bridging. The corresponding PluginModuleChild will live in the plugin
|
||||
* process. We use intr semantics here to ensure that the PluginModuleParent
|
||||
* allocation message is dispatched before LoadPlugin returns.
|
||||
*/
|
||||
intr LoadPlugin(uint32_t pluginId);
|
||||
|
||||
/**
|
||||
* This call returns the set of plugins loaded in the chrome
|
||||
* process. However, in many cases this set will not have changed since the
|
||||
* last FindPlugins message. Consequently, the chrome process increments an
|
||||
* epoch number every time the set of plugins changes. The content process
|
||||
* sends up the last epoch it observed. If the epochs are the same, the
|
||||
* chrome process returns no plugins. Otherwise it returns a complete list.
|
||||
*
|
||||
* |pluginEpoch| is the epoch last observed by the content
|
||||
* process. |newPluginEpoch| is the current epoch in the chrome process. If
|
||||
* |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
|
||||
*/
|
||||
sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch);
|
||||
|
||||
async PJavaScript();
|
||||
|
||||
sync PRemoteSpellcheckEngine();
|
||||
|
@ -499,8 +499,9 @@ void MediaDecoder::Shutdown()
|
||||
|
||||
ChangeState(PLAY_STATE_SHUTDOWN);
|
||||
|
||||
// If we hit this assertion, there might be a bug in network state transition.
|
||||
NS_ASSERTION(!mProgressTimer, "Progress timer should've been stopped.");
|
||||
if (mProgressTimer) {
|
||||
StopProgress();
|
||||
}
|
||||
mOwner = nullptr;
|
||||
|
||||
MediaShutdownManager::Instance().Unregister(this);
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
@ -36,7 +36,7 @@ MaybeFinish()
|
||||
FakeDecryptor::FakeDecryptor()
|
||||
: mCallback(nullptr)
|
||||
{
|
||||
assert(!sInstance);
|
||||
MOZ_ASSERT(!sInstance);
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ void FakeDecryptor::DecryptingComplete()
|
||||
void
|
||||
FakeDecryptor::Message(const std::string& aMessage)
|
||||
{
|
||||
assert(sInstance);
|
||||
MOZ_ASSERT(sInstance);
|
||||
const static std::string sid("fake-session-id");
|
||||
sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
|
||||
(const uint8_t*)aMessage.c_str(), aMessage.size(),
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
#include "gmp-test-storage.h"
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class WriteRecordClient : public GMPRecordClient {
|
||||
@ -119,7 +120,7 @@ GMPErr
|
||||
ReadRecord(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation)
|
||||
{
|
||||
assert(aContinuation);
|
||||
MOZ_ASSERT(aContinuation);
|
||||
GMPRecord* record;
|
||||
ReadRecordClient* client = new ReadRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
@ -140,14 +141,14 @@ GMPOpenRecord(const char* aName,
|
||||
GMPRecord** aOutRecord,
|
||||
GMPRecordClient* aClient)
|
||||
{
|
||||
assert(g_platform_api);
|
||||
MOZ_ASSERT(g_platform_api);
|
||||
return g_platform_api->createrecord(aName, aNameLength, aOutRecord, aClient);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
GMPRunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
assert(g_platform_api);
|
||||
MOZ_ASSERT(g_platform_api);
|
||||
return g_platform_api->runonmainthread(aTask);
|
||||
}
|
||||
|
||||
@ -180,7 +181,7 @@ GMPErr
|
||||
GMPOpenRecord(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation)
|
||||
{
|
||||
assert(aContinuation);
|
||||
MOZ_ASSERT(aContinuation);
|
||||
GMPRecord* record;
|
||||
OpenRecordClient* client = new OpenRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
|
@ -84,18 +84,6 @@ public:
|
||||
mTaskQueue = aTaskQueue;
|
||||
}
|
||||
|
||||
void BreakCycles()
|
||||
{
|
||||
if (mReader) {
|
||||
mReader->BreakCycles();
|
||||
mReader = nullptr;
|
||||
}
|
||||
mTaskQueue = nullptr;
|
||||
#ifdef MOZ_EME
|
||||
mCDMProxy = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
virtual nsresult SetCDMProxy(CDMProxy* aProxy)
|
||||
{
|
||||
|
@ -391,7 +391,7 @@ TrackBuffer::BreakCycles()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
mDecoders[i]->BreakCycles();
|
||||
mDecoders[i]->GetReader()->BreakCycles();
|
||||
}
|
||||
mDecoders.Clear();
|
||||
|
||||
|
@ -1,178 +0,0 @@
|
||||
const KEYSYSTEM_TYPE = "org.w3.clearkey";
|
||||
|
||||
function bail(message)
|
||||
{
|
||||
return function(err) {
|
||||
ok(false, message);
|
||||
if (err) {
|
||||
info(err);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function ArrayBufferToString(arr)
|
||||
{
|
||||
var str = '';
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < view.length; i++) {
|
||||
str += String.fromCharCode(view[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function StringToArrayBuffer(str)
|
||||
{
|
||||
var arr = new ArrayBuffer(str.length);
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
view[i] = str.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function Base64ToHex(str)
|
||||
{
|
||||
var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
|
||||
var res = "";
|
||||
for (var i = 0; i < bin.length; i++) {
|
||||
res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function HexToBase64(hex)
|
||||
{
|
||||
var bin = "";
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
function UpdateSessionFunc(test) {
|
||||
return function(ev) {
|
||||
var msgStr = ArrayBufferToString(ev.message);
|
||||
var msg = JSON.parse(msgStr);
|
||||
|
||||
info("got message from CDM: " + msgStr);
|
||||
is(msg.type, test.sessionType, "Key session type should match");
|
||||
ok(msg.kids, "message event should contain key ID array");
|
||||
|
||||
var outKeys = [];
|
||||
|
||||
for (var i = 0; i < msg.kids.length; i++) {
|
||||
var id64 = msg.kids[i];
|
||||
var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
|
||||
var key = test.keys[idHex];
|
||||
|
||||
if (key) {
|
||||
info("found key " + key + " for key id " + idHex);
|
||||
outKeys.push({
|
||||
"kty":"oct",
|
||||
"alg":"A128KW",
|
||||
"kid":id64,
|
||||
"k":HexToBase64(key)
|
||||
});
|
||||
} else {
|
||||
bail("Couldn't find key for key id " + idHex);
|
||||
}
|
||||
}
|
||||
|
||||
var update = JSON.stringify({
|
||||
"keys" : outKeys,
|
||||
"type" : msg.type
|
||||
});
|
||||
info("sending update message to CDM: " + update);
|
||||
|
||||
ev.target.update(StringToArrayBuffer(update)).then(function() {
|
||||
info("MediaKeySession update ok!");
|
||||
}, bail("MediaKeySession update failed"));
|
||||
}
|
||||
}
|
||||
|
||||
function PlayFragmented(test, elem)
|
||||
{
|
||||
return new Promise(function(resolve, reject) {
|
||||
var ms = new MediaSource();
|
||||
elem.src = URL.createObjectURL(ms);
|
||||
|
||||
var sb;
|
||||
var curFragment = 0;
|
||||
|
||||
function addNextFragment() {
|
||||
if (curFragment >= test.fragments.length) {
|
||||
ms.endOfStream();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentFile = test.fragments[curFragment++];
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", fragmentFile);
|
||||
req.responseType = "arraybuffer";
|
||||
|
||||
req.addEventListener("load", function() {
|
||||
sb.appendBuffer(new Uint8Array(req.response));
|
||||
});
|
||||
|
||||
info("fetching resource " + fragmentFile);
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
sb = ms.addSourceBuffer(test.type);
|
||||
sb.addEventListener("updateend", addNextFragment);
|
||||
|
||||
addNextFragment();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise that is resovled when the media element is ready to have
|
||||
// its play() function called; when it's loaded MSE fragments, or once the load
|
||||
// has started for non-MSE video.
|
||||
function LoadTest(test, elem)
|
||||
{
|
||||
if (test.fragments) {
|
||||
return PlayFragmented(test, elem);
|
||||
}
|
||||
|
||||
// This file isn't fragmented; set the media source normally.
|
||||
return new Promise(function(resolve, reject) {
|
||||
elem.src = test.name;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function SetupEME(test, token, params)
|
||||
{
|
||||
var v = document.createElement("video");
|
||||
|
||||
var onSetKeysFail = (params && params.onSetKeysFail)
|
||||
? params.onSetKeysFail
|
||||
: bail(token + " Failed to set MediaKeys on <video> element");
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
info(token + " got encrypted event");
|
||||
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
|
||||
info(token + " created MediaKeys object ok");
|
||||
mediaKeys.sessions = [];
|
||||
return v.setMediaKeys(mediaKeys);
|
||||
}, bail("failed to create MediaKeys object")).then(function() {
|
||||
info(token + " set MediaKeys on <video> element ok");
|
||||
|
||||
var session = v.mediaKeys.createSession(test.sessionType);
|
||||
if (params && params.onsessioncreated) {
|
||||
params.onsessioncreated(session);
|
||||
}
|
||||
session.addEventListener("message", UpdateSessionFunc(test));
|
||||
session.generateRequest(ev.initDataType, ev.initData).then(function() {
|
||||
}, bail(token + " Failed to initialise MediaKeySession"));
|
||||
|
||||
}, onSetKeysFail);
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
@ -124,7 +124,7 @@ var gPlayTests = [
|
||||
// With first frame a "duplicate" (empty) frame.
|
||||
{ name:"bug500311.ogv", type:"video/ogg", duration:1.96 },
|
||||
// Small audio file
|
||||
{ name:"small-shot.ogg", type:"video/ogg", duration:0.276 },
|
||||
{ name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
|
||||
// More audio in file than video.
|
||||
{ name:"short-video.ogv", type:"video/ogg", duration:1.081 },
|
||||
// First Theora data packet is zero bytes.
|
||||
@ -679,13 +679,17 @@ function checkMetadata(msg, e, test) {
|
||||
// Returns the first test from candidates array which we can play with the
|
||||
// installed video backends.
|
||||
function getPlayableVideo(candidates) {
|
||||
var v = document.createElement("video");
|
||||
var resources = candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
|
||||
var resources = getPlayableVideos(candidates);
|
||||
if (resources.length > 0)
|
||||
return resources[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPlayableVideos(candidates) {
|
||||
var v = document.createElement("video");
|
||||
return candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
|
||||
}
|
||||
|
||||
function getPlayableAudio(candidates) {
|
||||
var v = document.createElement("audio");
|
||||
var resources = candidates.filter(function(x){return /^audio/.test(x.type) && v.canPlayType(x.type);});
|
||||
|
@ -140,7 +140,6 @@ support-files =
|
||||
dirac.ogg^headers^
|
||||
dynamic_redirect.sjs
|
||||
dynamic_resource.sjs
|
||||
eme.js
|
||||
gizmo-frag-cenc1.m4s
|
||||
gizmo-frag-cenc2.m4s
|
||||
gizmo-frag-cencinit.mp4
|
||||
@ -361,11 +360,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_defaultMuted.html]
|
||||
[test_delay_load.html]
|
||||
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_playback.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_eme_stream_capture_blocked.html]
|
||||
[test_encryptedMediaExtensions.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||
[test_error_in_video_document.html]
|
||||
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
|
||||
@ -402,7 +397,6 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_mediarecorder_record_gum_video_timeslice.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
|
||||
[test_mediarecorder_record_immediate_stop.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_record_no_timeslice.html]
|
||||
skip-if = toolkit == 'gonk' && debug
|
||||
[test_mediarecorder_record_nosrc.html]
|
||||
|
@ -1,67 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(token);
|
||||
|
||||
var sessions = [];
|
||||
|
||||
var v = SetupEME(test, token);
|
||||
v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
|
||||
v.addEventListener("canplay", function(ev) {
|
||||
var video = ev.target;
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
document.body.appendChild(canvas);
|
||||
var ctx = canvas.getContext("2d");
|
||||
var threwError = false;
|
||||
try {
|
||||
ctx.drawImage(video, 0, 0);
|
||||
} catch (ex) {
|
||||
threwError = true;
|
||||
}
|
||||
ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
|
||||
manager.finished(token);
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
LoadTest(test, v);
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,128 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
|
||||
function KeysChangeFunc(session, keys, token) {
|
||||
session.keyIdsReceived = [];
|
||||
for (var keyid in keys) {
|
||||
info("Set " + keyid + " to false in session.keyIdsReceived");
|
||||
session.keyIdsReceived[keyid] = false;
|
||||
}
|
||||
return function(ev) {
|
||||
var session = ev.target;
|
||||
session.gotKeysChanged = true;
|
||||
session.getUsableKeyIds().then(function(keyIds) {
|
||||
for (var k = 0; k < keyIds.length; k++) {
|
||||
var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
|
||||
ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
|
||||
session.keyIdsReceived[kid] = true;
|
||||
}
|
||||
}, bail("Failed to get keyIds"));
|
||||
}
|
||||
}
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(token);
|
||||
|
||||
var sessions = [];
|
||||
|
||||
var v = SetupEME(test, token,
|
||||
{
|
||||
onsessioncreated: function(session) {
|
||||
sessions.push(session);
|
||||
session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var gotEncrypted = false;
|
||||
var gotPlaying = false;
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
token + " MediaKeys should support this keysystem");
|
||||
gotEncrypted = true;
|
||||
});
|
||||
|
||||
v.addEventListener("playing", function () { gotPlaying = true; });
|
||||
|
||||
v.addEventListener("ended", function(ev) {
|
||||
ok(true, token + " got ended event");
|
||||
manager.finished(token);
|
||||
|
||||
ok(gotEncrypted, token + " encrypted event should have fired");
|
||||
ok(gotPlaying, token + " playing event should have fired");
|
||||
|
||||
ok(Math.abs(test.duration - v.duration) < 0.1,
|
||||
token + " Duration of video should be corrrect");
|
||||
ok(Math.abs(test.duration - v.currentTime) < 0.1,
|
||||
token + " Current time should be same as duration");
|
||||
|
||||
// Verify all sessions had all keys went sent the to the CDM usable, and thus
|
||||
// that we received keyschange event(s).
|
||||
is(sessions.length, 1, "should have 1 session");
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
var session = sessions[i];
|
||||
ok(session.gotKeysChanged, token + " should have received at least one keychange event");
|
||||
for (var kid in session.keyIdsReceived) {
|
||||
ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
|
||||
}
|
||||
|
||||
function testIsTypeSupported()
|
||||
{
|
||||
var t = MediaKeys.isTypeSupported;
|
||||
const clearkey = "org.w3.clearkey";
|
||||
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
|
||||
ok(t(clearkey), "ClearKey supported.");
|
||||
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
|
||||
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
|
||||
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
testIsTypeSupported();
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,102 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
// Three cases:
|
||||
// 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
|
||||
// 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
|
||||
// 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
|
||||
|
||||
// Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
|
||||
var case1token = token + "_case1";
|
||||
var setKeysFailed = function() {
|
||||
ok(true, case1token + " setMediaKeys failed as expected.");
|
||||
manager.finished(case1token);
|
||||
};
|
||||
var v1 = SetupEME(test, case1token, { onSetKeysFail: setKeysFailed });
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(v1);
|
||||
v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v1.addEventListener("error", bail(case1token + " got error event"));
|
||||
v1.addEventListener("canplay", function(ev) {
|
||||
ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
|
||||
});
|
||||
manager.started(case1token);
|
||||
LoadTest(test, v1);
|
||||
|
||||
|
||||
// Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
|
||||
var case2token = token + "_case2";
|
||||
var v2 = SetupEME(test, case2token);
|
||||
v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v2.addEventListener("error", bail(case2token + " got error event"));
|
||||
v2.addEventListener("canplay", function(ev) {
|
||||
ok(true, case2token + " should reach canplay");
|
||||
var threw = false;
|
||||
try {
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(v2);
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
|
||||
manager.finished(case2token);
|
||||
});
|
||||
manager.started(case2token);
|
||||
LoadTest(test, v2);
|
||||
|
||||
|
||||
// Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
|
||||
var case3token = token + "_case3";
|
||||
var v3 = SetupEME(test, case3token);
|
||||
v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
|
||||
v3.addEventListener("error", bail(case3token + " got error event"));
|
||||
v3.addEventListener("canplay", function(ev) {
|
||||
ok(true, case3token + " should reach canplay");
|
||||
var threw = false;
|
||||
try {
|
||||
var stream = v3.mozCaptureStreamUntilEnded();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
|
||||
manager.finished(case3token);
|
||||
});
|
||||
manager.started(case3token);
|
||||
LoadTest(test, v3);
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
278
dom/media/test/test_encryptedMediaExtensions.html
Normal file
@ -0,0 +1,278 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
const KEYSYSTEM_TYPE = "org.w3.clearkey";
|
||||
|
||||
function bail(message)
|
||||
{
|
||||
return function(err) {
|
||||
ok(false, message);
|
||||
if (err) {
|
||||
info(err);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function ArrayBufferToString(arr)
|
||||
{
|
||||
var str = '';
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < view.length; i++) {
|
||||
str += String.fromCharCode(view[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function StringToArrayBuffer(str)
|
||||
{
|
||||
var arr = new ArrayBuffer(str.length);
|
||||
var view = new Uint8Array(arr);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
view[i] = str.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function Base64ToHex(str)
|
||||
{
|
||||
var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
|
||||
var res = "";
|
||||
for (var i = 0; i < bin.length; i++) {
|
||||
res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function HexToBase64(hex)
|
||||
{
|
||||
var bin = "";
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
function UpdateSessionFunc(test) {
|
||||
return function(ev) {
|
||||
var msgStr = ArrayBufferToString(ev.message);
|
||||
var msg = JSON.parse(msgStr);
|
||||
|
||||
info("got message from CDM: " + msgStr);
|
||||
is(msg.type, test.sessionType, "Key session type should match");
|
||||
ok(msg.kids, "message event should contain key ID array");
|
||||
|
||||
var outKeys = [];
|
||||
|
||||
for (var i = 0; i < msg.kids.length; i++) {
|
||||
var id64 = msg.kids[i];
|
||||
var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
|
||||
var key = test.keys[idHex];
|
||||
|
||||
if (key) {
|
||||
info("found key " + key + " for key id " + idHex);
|
||||
outKeys.push({
|
||||
"kty":"oct",
|
||||
"alg":"A128KW",
|
||||
"kid":id64,
|
||||
"k":HexToBase64(key)
|
||||
});
|
||||
} else {
|
||||
bail("Couldn't find key for key id " + idHex);
|
||||
}
|
||||
}
|
||||
|
||||
var update = JSON.stringify({
|
||||
"keys" : outKeys,
|
||||
"type" : msg.type
|
||||
});
|
||||
info("sending update message to CDM: " + update);
|
||||
|
||||
ev.target.update(StringToArrayBuffer(update)).then(function() {
|
||||
info("MediaKeySession update ok!");
|
||||
}, bail("MediaKeySession update failed"));
|
||||
}
|
||||
}
|
||||
|
||||
function PlayFragmented(test, elem)
|
||||
{
|
||||
var ms = new MediaSource();
|
||||
elem.src = URL.createObjectURL(ms);
|
||||
|
||||
var sb;
|
||||
var curFragment = 0;
|
||||
|
||||
function addNextFragment() {
|
||||
if (curFragment >= test.fragments.length) {
|
||||
ms.endOfStream();
|
||||
elem.play();
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentFile = test.fragments[curFragment++];
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", fragmentFile);
|
||||
req.responseType = "arraybuffer";
|
||||
|
||||
req.addEventListener("load", function() {
|
||||
sb.appendBuffer(new Uint8Array(req.response));
|
||||
});
|
||||
|
||||
info("fetching resource " + fragmentFile);
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
sb = ms.addSourceBuffer(test.type);
|
||||
sb.addEventListener("updateend", addNextFragment);
|
||||
|
||||
addNextFragment();
|
||||
});
|
||||
}
|
||||
|
||||
function PlayTest(test, elem)
|
||||
{
|
||||
if (test.fragments) {
|
||||
PlayFragmented(test, elem);
|
||||
return;
|
||||
}
|
||||
|
||||
// This file isn't fragmented; set the media source normally.
|
||||
elem.src = test.name;
|
||||
elem.play();
|
||||
}
|
||||
|
||||
function KeysChangeFunc(session, keys) {
|
||||
session.keyIdsReceived = [];
|
||||
for (var keyid in keys) {
|
||||
info("Set " + keyid + " to false in session.keyIdsReceived");
|
||||
session.keyIdsReceived[keyid] = false;
|
||||
}
|
||||
return function(ev) {
|
||||
var session = ev.target;
|
||||
session.gotKeysChanged = true;
|
||||
session.getUsableKeyIds().then(function(keyIds) {
|
||||
for (var k = 0; k < keyIds.length; k++) {
|
||||
var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
|
||||
ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
|
||||
session.keyIdsReceived[kid] = true;
|
||||
}
|
||||
}, bail("Failed to get keyIds"));
|
||||
}
|
||||
}
|
||||
|
||||
function startTest(test, token)
|
||||
{
|
||||
manager.started(test._token);
|
||||
|
||||
var v = document.createElement("video");
|
||||
var gotEncrypted = false;
|
||||
var gotPlaying = false;
|
||||
|
||||
v.addEventListener("encrypted", function(ev) {
|
||||
gotEncrypted = true;
|
||||
|
||||
info(token + " got encrypted event");
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
token + " MediaKeys should support this keysystem");
|
||||
|
||||
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
|
||||
info(token + " created MediaKeys object ok");
|
||||
mediaKeys.sessions = [];
|
||||
return v.setMediaKeys(mediaKeys);
|
||||
}, bail("failed to create MediaKeys object")).then(function() {
|
||||
info(token + " set MediaKeys on <video> element ok");
|
||||
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
"MediaKeys should still support keysystem after CDM created...");
|
||||
|
||||
var session = v.mediaKeys.createSession(test.sessionType);
|
||||
v.mediaKeys.sessions.push(session);
|
||||
session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
|
||||
session.addEventListener("message", UpdateSessionFunc(test));
|
||||
session.generateRequest(ev.initDataType, ev.initData).then(function() {
|
||||
}, bail(token + " Failed to initialise MediaKeySession"));
|
||||
|
||||
}, bail(token + " Failed to set MediaKeys on <video> element"));
|
||||
});
|
||||
|
||||
v.addEventListener("playing", function () { gotPlaying = true; });
|
||||
|
||||
v.addEventListener("ended", function(ev) {
|
||||
ok(true, token + " got ended event");
|
||||
manager.finished(test._token);
|
||||
|
||||
ok(gotEncrypted, token + " encrypted event should have fired");
|
||||
ok(gotPlaying, token + " playing event should have fired");
|
||||
|
||||
ok(Math.abs(test.duration - v.duration) < 0.1,
|
||||
token + " Duration of video should be corrrect");
|
||||
ok(Math.abs(test.duration - v.currentTime) < 0.1,
|
||||
token + " Current time should be same as duration");
|
||||
// Verify all sessions had all keys went sent the to the CDM usable, and thus
|
||||
// that we received keyschange event(s).
|
||||
var sessions = v.mediaKeys.sessions;
|
||||
is(sessions.length, 1, "should have 1 session");
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
var session = sessions[i];
|
||||
ok(session.gotKeysChanged, "Should have received at least one keychange event");
|
||||
for (var kid in session.keyIdsReceived) {
|
||||
ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
v.addEventListener("error", bail(token + " got error event"));
|
||||
|
||||
PlayTest(test, v);
|
||||
}
|
||||
|
||||
function testIsTypeSupported()
|
||||
{
|
||||
var t = MediaKeys.isTypeSupported;
|
||||
const clearkey = "org.w3.clearkey";
|
||||
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
|
||||
ok(t(clearkey), "ClearKey supported.");
|
||||
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
|
||||
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
|
||||
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
testIsTypeSupported();
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
var prefs = [
|
||||
[ "media.mediasource.enabled", true ],
|
||||
[ "media.mediasource.ignore_codecs", true ],
|
||||
];
|
||||
|
||||
if (/Linux/.test(navigator.userAgent) ||
|
||||
!document.createElement('video').canPlayType("video/mp4")) {
|
||||
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -14,27 +14,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=804875
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=804875">Mozilla Bug 804875</a>
|
||||
|
||||
<video style="border: 4px solid red" controls></video>
|
||||
<canvas></canvas>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function finish(v) {
|
||||
removeNodeAndSource(v);
|
||||
manager.finished(v.token);
|
||||
}
|
||||
|
||||
function onLoadedMetadata_Audio(e) {
|
||||
var t = e.target;
|
||||
is(t.videoHeight, 0, "videoHeight should be zero when there is no video.");
|
||||
is(t.videoWidth, 0, "videoWidth should be zero when there is no video.");
|
||||
is(t.mozPaintedFrames, 0, "mozPaintedFrames should be zero when there is no video.");
|
||||
is(t.mozFrameDelay, 0, "mozFrameDelay should be zero when there is no video.");
|
||||
is(t.videoHeight, 0, t.name + ": videoHeight should be zero when there is no video.");
|
||||
is(t.videoWidth, 0, t.name + ": videoWidth should be zero when there is no video.");
|
||||
is(t.mozPaintedFrames, 0, t.name + ": mozPaintedFrames should be zero when there is no video.");
|
||||
is(t.mozFrameDelay, 0, t.name + ": mozFrameDelay should be zero when there is no video.");
|
||||
var c = document.getElementsByTagName("canvas")[0].getContext("2d");
|
||||
try {
|
||||
c.drawImage(t, 0, 0, t.videoHeight, t.videoWidth);
|
||||
} catch (e) {
|
||||
ok(true, "Trying to draw to a canvas should throw, since we don't have a frame anymore");
|
||||
SimpleTest.finish();
|
||||
ok(true, t.name + ": Trying to draw to a canvas should throw, since we don't have a frame anymore");
|
||||
finish(t);
|
||||
return;
|
||||
}
|
||||
ok(false, "We should not succeed to draw a video frame on the canvas.");
|
||||
ok(false, t.name + ": We should not succeed to draw a video frame on the canvas.");
|
||||
}
|
||||
|
||||
function onTimeUpdate_Video(e) {
|
||||
@ -43,17 +49,17 @@ function onTimeUpdate_Video(e) {
|
||||
return;
|
||||
}
|
||||
t.removeEventListener("timeupdate", onTimeUpdate_Video);
|
||||
ok(t.mozPaintedFrames > 0, "mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
|
||||
ok(t.mozFrameDelay >= 0, "mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
|
||||
ok(t.mozPaintedFrames > 0, t.name + ": mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
|
||||
ok(t.mozFrameDelay >= 0, t.name + ": mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
|
||||
|
||||
if (v._firstTime) {
|
||||
if (t._firstTime) {
|
||||
t.src = t.src;
|
||||
v._firstTime = false;
|
||||
t._firstTime = false;
|
||||
} else {
|
||||
var source = getPlayableAudio(gPlayTests);
|
||||
if (!source) {
|
||||
todo("No audio file available.")
|
||||
SimpleTest.finish();
|
||||
finish(t);
|
||||
} else {
|
||||
t.removeEventListener("loadedmetadata", onLoadedMetadata_Video);
|
||||
t.addEventListener("loadedmetadata", onLoadedMetadata_Audio);
|
||||
@ -64,22 +70,25 @@ function onTimeUpdate_Video(e) {
|
||||
|
||||
function onLoadedMetadata_Video(e) {
|
||||
var t = e.target;
|
||||
ok(t.videoHeight != 0, "We should have a videoHeight.");
|
||||
ok(t.videoWidth != 0, "We should have a videoWidth.");
|
||||
isnot(t.videoHeight, 0, t.name + ": We should have a videoHeight.");
|
||||
isnot(t.videoWidth, 0, t.name + ": We should have a videoWidth.");
|
||||
t.addEventListener("timeupdate", onTimeUpdate_Video);
|
||||
t.play();
|
||||
}
|
||||
|
||||
var v = document.getElementsByTagName("video")[0];
|
||||
v._firstTime = true;
|
||||
var source = getPlayableVideo(gPlayTests);
|
||||
if (!source) {
|
||||
todo("No video file available.");
|
||||
} else {
|
||||
function startTest(test, token) {
|
||||
var v = document.createElement('video');
|
||||
document.body.appendChild(v);
|
||||
v.preload = "metadata";
|
||||
v._firstTime = true;
|
||||
v.addEventListener("loadedmetadata", onLoadedMetadata_Video);
|
||||
v.src = source.name;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
v.src = test.name;
|
||||
v.token = token;
|
||||
v.name = test.name;
|
||||
manager.started(token);
|
||||
}
|
||||
|
||||
manager.runTests(getPlayableVideos(gSmallTests.concat(gSeekTests)), startTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -285,12 +285,6 @@ AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef MOZ_EME
|
||||
if (aMediaElement.ContainsRestrictedContent()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
|
@ -80,7 +80,8 @@ using mozilla::PluginLibrary;
|
||||
using mozilla::PluginPRLibrary;
|
||||
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
using mozilla::plugins::PluginModuleParent;
|
||||
using mozilla::plugins::PluginModuleChromeParent;
|
||||
using mozilla::plugins::PluginModuleContentParent;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include "mozilla/X11Util.h"
|
||||
@ -247,6 +248,10 @@ nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID,
|
||||
bool
|
||||
nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
|
||||
{
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
// We force OOP on Linux/GTK3 because some plugins use GTK2 and both GTK
|
||||
// libraries can't be loaded in the same process.
|
||||
@ -388,8 +393,12 @@ GetNewPluginLibrary(nsPluginTag *aPluginTag)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
return PluginModuleContentParent::LoadModule(aPluginTag->mId);
|
||||
}
|
||||
|
||||
if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
|
||||
return PluginModuleParent::LoadModule(aPluginTag->mFullPath.get());
|
||||
return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId);
|
||||
}
|
||||
return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
|
||||
}
|
||||
|
@ -48,7 +48,10 @@
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsPluginStreamListenerPeer.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/plugins/PluginBridge.h"
|
||||
#include "mozilla/plugins/PluginTypes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "nsEnumeratorUtils.h"
|
||||
@ -106,6 +109,7 @@
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::TimeStamp;
|
||||
using mozilla::plugins::PluginTag;
|
||||
|
||||
// Null out a strong ref to a linked list iteratively to avoid
|
||||
// exhausting the stack (bug 486349).
|
||||
@ -309,6 +313,10 @@ bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aPluginTag->mContentProcessRunningCount) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mInstances.Length(); i++) {
|
||||
nsNPAPIPluginInstance *instance = mInstances[i].get();
|
||||
if (instance &&
|
||||
@ -1272,6 +1280,76 @@ nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
// If plugins haven't been scanned yet, do so now
|
||||
LoadPlugins();
|
||||
|
||||
nsPluginTag* pluginTag = PluginWithId(aPluginId);
|
||||
if (pluginTag) {
|
||||
nsresult rv = EnsurePluginLoaded(pluginTag);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We only get here if a content process doesn't have a PluginModuleParent
|
||||
// for the given plugin already. Therefore, this counter is counting the
|
||||
// number of outstanding PluginModuleParents for the plugin, excluding the
|
||||
// one from the chrome process.
|
||||
pluginTag->mContentProcessRunningCount++;
|
||||
NS_ADDREF(*aPlugin = pluginTag->mPlugin);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
class nsPluginUnloadRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
||||
if (!host) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
|
||||
if (!pluginTag) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
|
||||
pluginTag->mContentProcessRunningCount--;
|
||||
|
||||
if (!pluginTag->mContentProcessRunningCount) {
|
||||
if (!host->IsRunningPlugin(pluginTag)) {
|
||||
pluginTag->TryUnloadPlugin(false);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t mPluginId;
|
||||
};
|
||||
|
||||
void
|
||||
nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
// This is called in response to a message from the plugin. Don't unload the
|
||||
// plugin until the message handler is off the stack.
|
||||
nsRefPtr<nsPluginUnloadRunnable> runnable =
|
||||
new nsPluginUnloadRunnable(aPluginId);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
@ -1614,6 +1692,17 @@ nsPluginHost::FirstPluginWithPath(const nsCString& path)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPluginTag*
|
||||
nsPluginHost::PluginWithId(uint32_t aId)
|
||||
{
|
||||
for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
|
||||
if (tag->mId == aId) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
|
||||
@ -1700,12 +1789,32 @@ struct CompareFilesByTime
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
|
||||
{
|
||||
aPluginTag->mNext = mPlugins;
|
||||
mPlugins = aPluginTag;
|
||||
|
||||
if (aPluginTag->IsActive()) {
|
||||
nsAdoptingCString disableFullPage =
|
||||
Preferences::GetCString(kPrefDisableFullPage);
|
||||
for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
|
||||
if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
|
||||
RegisterWithCategoryManager(aPluginTag->mMimeTypes[i],
|
||||
ePluginRegister);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
|
||||
|
||||
nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
bool aCreatePluginList,
|
||||
bool *aPluginsChanged)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aPluginsChanged);
|
||||
nsresult rv;
|
||||
|
||||
@ -1895,42 +2004,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Add plugin tags such that the list is ordered by modification date,
|
||||
// newest to oldest. This is ugly, it'd be easier with just about anything
|
||||
// other than a single-directional linked list.
|
||||
if (mPlugins) {
|
||||
nsPluginTag *prev = nullptr;
|
||||
nsPluginTag *next = mPlugins;
|
||||
while (next) {
|
||||
if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
|
||||
pluginTag->mNext = next;
|
||||
if (prev) {
|
||||
prev->mNext = pluginTag;
|
||||
} else {
|
||||
mPlugins = pluginTag;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = next;
|
||||
next = prev->mNext;
|
||||
if (!next) {
|
||||
prev->mNext = pluginTag;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mPlugins = pluginTag;
|
||||
}
|
||||
|
||||
if (pluginTag->IsActive()) {
|
||||
nsAdoptingCString disableFullPage =
|
||||
Preferences::GetCString(kPrefDisableFullPage);
|
||||
for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
|
||||
if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
|
||||
RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
|
||||
ePluginRegister);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddPluginTag(pluginTag);
|
||||
}
|
||||
|
||||
if (warnOutdated) {
|
||||
@ -1944,6 +2018,8 @@ nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
|
||||
bool aCreatePluginList,
|
||||
bool *aPluginsChanged)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
@ -1968,6 +2044,34 @@ nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginHost::IncrementChromeEpoch()
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
mPluginEpoch++;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsPluginHost::ChromeEpoch()
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
return mPluginEpoch;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsPluginHost::ChromeEpochForContent()
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
return mPluginEpoch;
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
mPluginEpoch = aEpoch;
|
||||
}
|
||||
|
||||
nsresult nsPluginHost::LoadPlugins()
|
||||
{
|
||||
#ifdef ANDROID
|
||||
@ -1990,6 +2094,10 @@ nsresult nsPluginHost::LoadPlugins()
|
||||
|
||||
// only if plugins have changed will we notify plugin-change observers
|
||||
if (pluginschanged) {
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
IncrementChromeEpoch();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (obsService)
|
||||
@ -1999,6 +2107,54 @@ nsresult nsPluginHost::LoadPlugins()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
|
||||
dom::ContentChild* cp = dom::ContentChild::GetSingleton();
|
||||
nsTArray<PluginTag> plugins;
|
||||
uint32_t parentEpoch;
|
||||
if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (parentEpoch != ChromeEpochForContent()) {
|
||||
SetChromeEpochForContent(parentEpoch);
|
||||
*aPluginsChanged = true;
|
||||
if (!aCreatePluginList) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < plugins.Length(); i++) {
|
||||
PluginTag& tag = plugins[i];
|
||||
|
||||
// Don't add the same plugin again.
|
||||
if (PluginWithId(tag.id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
|
||||
tag.name().get(),
|
||||
tag.description().get(),
|
||||
tag.filename().get(),
|
||||
"", // aFullPath
|
||||
tag.version().get(),
|
||||
nsTArray<nsCString>(tag.mimeTypes()),
|
||||
nsTArray<nsCString>(tag.mimeDescriptions()),
|
||||
nsTArray<nsCString>(tag.extensions()),
|
||||
tag.isJavaPlugin(),
|
||||
tag.isFlashPlugin(),
|
||||
tag.lastModifiedTime(),
|
||||
tag.isFromExtension());
|
||||
AddPluginTag(pluginTag);
|
||||
}
|
||||
}
|
||||
|
||||
mPluginsLoaded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// if aCreatePluginList is false we will just scan for plugins
|
||||
// and see if any changes have been made to the plugins.
|
||||
// This is needed in ReloadPlugins to prevent possible recursive reloads
|
||||
@ -2009,6 +2165,11 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange
|
||||
NS_ENSURE_ARG_POINTER(aPluginsChanged);
|
||||
|
||||
*aPluginsChanged = false;
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Read cached plugins info. If the profile isn't yet available then don't
|
||||
@ -2167,9 +2328,58 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
||||
host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
// Load plugins so that the epoch is correct.
|
||||
LoadPlugins();
|
||||
|
||||
*aNewPluginEpoch = ChromeEpoch();
|
||||
if (aPluginEpoch == ChromeEpoch()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<nsPluginTag>> plugins;
|
||||
GetPlugins(plugins);
|
||||
|
||||
for (size_t i = 0; i < plugins.Length(); i++) {
|
||||
nsRefPtr<nsPluginTag> tag = plugins[i];
|
||||
aPlugins->AppendElement(PluginTag(tag->mId,
|
||||
tag->mName,
|
||||
tag->mDescription,
|
||||
tag->mMimeTypes,
|
||||
tag->mMimeDescriptions,
|
||||
tag->mExtensions,
|
||||
tag->mIsJavaPlugin,
|
||||
tag->mIsFlashPlugin,
|
||||
tag->mFileName,
|
||||
tag->mVersion,
|
||||
tag->mLastModifiedTime,
|
||||
tag->IsFromExtension()));
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
ReadPluginInfo();
|
||||
WritePluginInfo();
|
||||
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
|
||||
@ -2261,6 +2471,7 @@ nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
|
||||
nsresult
|
||||
nsPluginHost::WritePluginInfo()
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
|
||||
@ -2405,6 +2616,8 @@ nsPluginHost::WritePluginInfo()
|
||||
nsresult
|
||||
nsPluginHost::ReadPluginInfo()
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
|
||||
const long PLUGIN_REG_MAX_MIMETYPES = 1000;
|
||||
|
||||
@ -3542,6 +3755,7 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
|
||||
// instance of this plugin we reload it (launch a new plugin process).
|
||||
|
||||
crashedPluginTag->mPlugin = nullptr;
|
||||
crashedPluginTag->mContentProcessRunningCount = 0;
|
||||
|
||||
#ifdef XP_WIN
|
||||
CheckForDisabledWindows();
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsCRT.h"
|
||||
#include "mozilla/plugins/PluginTypes.h"
|
||||
|
||||
class nsNPAPIPlugin;
|
||||
class nsIComponentManager;
|
||||
@ -36,6 +37,7 @@ class nsIChannel;
|
||||
class nsPluginNativeWindow;
|
||||
class nsObjectLoadingContent;
|
||||
class nsPluginInstanceOwner;
|
||||
class nsPluginUnloadRunnable;
|
||||
class nsNPAPIPluginInstance;
|
||||
class nsNPAPIPluginStreamListener;
|
||||
class nsIPluginInstanceOwner;
|
||||
@ -88,6 +90,9 @@ public:
|
||||
nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
|
||||
|
||||
void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray);
|
||||
void FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<mozilla::plugins::PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch);
|
||||
|
||||
nsresult GetURL(nsISupports* pluginInst,
|
||||
const char* url,
|
||||
@ -191,12 +196,16 @@ public:
|
||||
nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
|
||||
|
||||
nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
|
||||
nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
|
||||
void NotifyContentModuleDestroyed(uint32_t aPluginId);
|
||||
|
||||
nsresult NewPluginStreamListener(nsIURI* aURL,
|
||||
nsNPAPIPluginInstance* aInstance,
|
||||
nsIStreamListener **aStreamListener);
|
||||
|
||||
private:
|
||||
friend class nsPluginUnloadRunnable;
|
||||
|
||||
nsresult
|
||||
TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
|
||||
|
||||
@ -214,6 +223,8 @@ private:
|
||||
nsresult
|
||||
FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
|
||||
|
||||
nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
|
||||
|
||||
nsresult
|
||||
FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
|
||||
|
||||
@ -222,6 +233,8 @@ private:
|
||||
enum nsRegisterType { ePluginRegister, ePluginUnregister };
|
||||
void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType);
|
||||
|
||||
void AddPluginTag(nsPluginTag* aPluginTag);
|
||||
|
||||
nsresult
|
||||
ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
bool aCreatePluginList,
|
||||
@ -255,11 +268,23 @@ private:
|
||||
|
||||
// Returns the first plugin at |path|
|
||||
nsPluginTag* FirstPluginWithPath(const nsCString& path);
|
||||
nsPluginTag* PluginWithId(uint32_t aId);
|
||||
|
||||
nsresult EnsurePrivateDirServiceProvider();
|
||||
|
||||
void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
|
||||
|
||||
// To be used by the chrome process whenever the set of plugins changes.
|
||||
void IncrementChromeEpoch();
|
||||
|
||||
// To be used by the chrome process; returns the current epoch.
|
||||
uint32_t ChromeEpoch();
|
||||
|
||||
// To be used by the content process to get/set the last observed epoch value
|
||||
// from the chrome process.
|
||||
uint32_t ChromeEpochForContent();
|
||||
void SetChromeEpochForContent(uint32_t aEpoch);
|
||||
|
||||
nsRefPtr<nsPluginTag> mPlugins;
|
||||
nsRefPtr<nsPluginTag> mCachedPlugins;
|
||||
nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
|
||||
@ -295,6 +320,12 @@ private:
|
||||
|
||||
nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
|
||||
|
||||
// This epoch increases each time we load the list of plugins from disk.
|
||||
// In the chrome process, this stores the actual epoch.
|
||||
// In the content process, this stores the last epoch value observed
|
||||
// when reading plugins from chrome.
|
||||
uint32_t mPluginEpoch;
|
||||
|
||||
static nsIFile *sPluginTempDir;
|
||||
|
||||
// We need to hold a global ptr to ourselves because we register for
|
||||
|
@ -66,10 +66,14 @@ GetStatePrefNameForPlugin(nsPluginTag* aTag)
|
||||
|
||||
/* nsPluginTag */
|
||||
|
||||
uint32_t nsPluginTag::sNextId;
|
||||
|
||||
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension)
|
||||
: mName(aPluginInfo->fName),
|
||||
: mId(sNextId++),
|
||||
mContentProcessRunningCount(0),
|
||||
mName(aPluginInfo->fName),
|
||||
mDescription(aPluginInfo->fDescription),
|
||||
mLibrary(nullptr),
|
||||
mIsJavaPlugin(false),
|
||||
@ -103,7 +107,9 @@ nsPluginTag::nsPluginTag(const char* aName,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension,
|
||||
bool aArgsAreUTF8)
|
||||
: mName(aName),
|
||||
: mId(sNextId++),
|
||||
mContentProcessRunningCount(0),
|
||||
mName(aName),
|
||||
mDescription(aDescription),
|
||||
mLibrary(nullptr),
|
||||
mIsJavaPlugin(false),
|
||||
@ -124,6 +130,39 @@ nsPluginTag::nsPluginTag(const char* aName,
|
||||
FixupVersion();
|
||||
}
|
||||
|
||||
nsPluginTag::nsPluginTag(uint32_t aId,
|
||||
const char* aName,
|
||||
const char* aDescription,
|
||||
const char* aFileName,
|
||||
const char* aFullPath,
|
||||
const char* aVersion,
|
||||
nsTArray<nsCString> aMimeTypes,
|
||||
nsTArray<nsCString> aMimeDescriptions,
|
||||
nsTArray<nsCString> aExtensions,
|
||||
bool aIsJavaPlugin,
|
||||
bool aIsFlashPlugin,
|
||||
int64_t aLastModifiedTime,
|
||||
bool aFromExtension)
|
||||
: mId(aId),
|
||||
mContentProcessRunningCount(0),
|
||||
mName(aName),
|
||||
mDescription(aDescription),
|
||||
mMimeTypes(aMimeTypes),
|
||||
mMimeDescriptions(aMimeDescriptions),
|
||||
mExtensions(aExtensions),
|
||||
mLibrary(nullptr),
|
||||
mIsJavaPlugin(aIsJavaPlugin),
|
||||
mIsFlashPlugin(aIsFlashPlugin),
|
||||
mFileName(aFileName),
|
||||
mVersion(aVersion),
|
||||
mLastModifiedTime(aLastModifiedTime),
|
||||
mNiceFileName(),
|
||||
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
|
||||
mCachedBlocklistStateValid(false),
|
||||
mIsFromExtension(aFromExtension)
|
||||
{
|
||||
}
|
||||
|
||||
nsPluginTag::~nsPluginTag()
|
||||
{
|
||||
NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
|
||||
|