Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-10-29 16:53:59 -04:00
commit b278f8c9e3
207 changed files with 5250 additions and 2779 deletions

View File

@ -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"
}
]

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
},

View File

@ -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
}));
}

View File

@ -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
}));
}

View File

@ -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
}),

View File

@ -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"));
},
/**

View File

@ -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

View File

@ -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() {

View File

@ -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() {

View File

@ -45,7 +45,7 @@ describe("loop.Dispatcher", function () {
beforeEach(function() {
gatherAction = new sharedActions.GatherCallData({
callId: "42",
windowId: "42",
outgoing: false
});

View 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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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]

View File

@ -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
}
},

View File

@ -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')

View File

@ -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;"

View File

@ -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

View File

@ -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;"

View File

@ -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);

View File

@ -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

View File

@ -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;
});

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View 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]();
});

View File

@ -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);
}

View File

@ -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")

View File

@ -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…

View File

@ -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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 B

View File

@ -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 ***/

View File

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -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);
}
}

View File

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -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 ***/

View File

@ -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)

View File

@ -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 {

View File

@ -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

View 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);
});
}

View 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);
});
}

View 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>

View File

@ -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()) {

View File

@ -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);

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -130,6 +130,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
'/dom/html',
'/dom/settings',

View File

@ -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();

View File

@ -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

View File

@ -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,
};

View 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>

View 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>

View 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>

View 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>

View File

@ -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

View File

@ -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...

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -25,7 +25,7 @@ CrashReporterChild::GetCrashReporter()
break;
}
case GeckoProcessType_Plugin: {
PluginModuleChild* child = PluginModuleChild::current();
PluginModuleChild* child = PluginModuleChild::GetChrome();
reporters = &child->ManagedPCrashReporterChild();
break;
}

View File

@ -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();

View File

@ -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);

View File

@ -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(),

View File

@ -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(),

View File

@ -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)
{

View File

@ -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();

View File

@ -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;
}

View File

@ -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);});

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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;

View File

@ -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);
}

View File

@ -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();

View File

@ -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

View File

@ -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");

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