Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-12-08 15:49:07 -05:00
commit 219d9e1072
34 changed files with 619 additions and 232 deletions

View File

@ -22,6 +22,10 @@ const Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyServiceGetter(this, "settings",
"@mozilla.org/settingsService;1",
"nsISettingsService");
function debug(msg) {
log(msg);
}
@ -89,14 +93,19 @@ ProcessGlobal.prototype = {
}
},
processWipeFile: function(text) {
log("processWipeFile " + text);
processCommandsFile: function(text) {
log("processCommandsFile " + text);
let lines = text.split("\n");
lines.forEach((line) => {
log(line);
let params = line.split(" ");
if (params[0] == "wipe") {
this.wipeDir(params[1]);
} else if (params[0] == "root") {
log("unrestrict devtools");
Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", false);
let lock = settings.createLock();
lock.set("developer.menu.enabled", true, null);
}
});
},
@ -113,7 +122,7 @@ ProcessGlobal.prototype = {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(postResetFile);
if (!file.exists()) {
debug("Nothing to wipe.")
debug("No additional command.")
return;
}
@ -122,7 +131,7 @@ ProcessGlobal.prototype = {
(array) => {
file.remove(false);
let decoder = new TextDecoder();
this.processWipeFile(decoder.decode(array));
this.processCommandsFile(decoder.decode(array));
},
function onError(error) {
debug("Error: " + error);

View File

@ -81,19 +81,24 @@ RecoveryService.prototype = {
}
log("factoryReset " + reason);
let commands = [];
if (reason == "wipe") {
let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
.getService(Ci.nsIVolumeService);
let volNames = volumeService.getVolumeNames();
log("Found " + volNames.length + " volumes");
let text = "";
for (let i = 0; i < volNames.length; i++) {
let name = volNames.queryElementAt(i, Ci.nsISupportsString);
let volume = volumeService.getVolumeByName(name.data);
log("Got volume: " + name.data + " at " + volume.mountPoint);
text += "wipe " + volume.mountPoint + "\n";
commands.push("wipe " + volume.mountPoint);
}
} else if (reason == "root") {
commands.push("root");
}
if (commands.length > 0) {
Cu.import("resource://gre/modules/osfile.jsm");
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
dir.initWithPath("/persist");
@ -101,6 +106,7 @@ RecoveryService.prototype = {
OS.Path.join("/persist", gFactoryResetFile):
OS.Path.join("/cache", gFactoryResetFile);
let encoder = new TextEncoder();
let text = commands.join("\n");
let array = encoder.encode(text);
let promise = OS.File.writeAtomic(postResetFile, array,
{ tmpPath: postResetFile + ".tmp" });

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "5f064a62ce5ee63c6f75ff3422a7472bec71f7bf",
"revision": "0d6dca3dab03252cef2eb22079d0cab6362eda1e",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a3e4614c7fb0a6ffa8c748bf5d49b34612c9d6d4"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6cfde14a9fc87659963092a6b9d7898a8c23ec74"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd4dcc8c4582e2368b47b0e62506d3031fb2fc09"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -346,6 +346,7 @@ let LoopRoomsInternal = {
.then(response => {
this.rooms.delete(roomToken);
eventEmitter.emit("delete", room);
eventEmitter.emit("delete:" + room.roomToken, room);
callback(null, room);
}, error => callback(error)).catch(error => callback(error));
},

View File

@ -211,6 +211,8 @@ loop.store.ActiveRoomStore = (function() {
this._mozLoop.rooms.on("update:" + actionData.roomToken,
this._handleRoomUpdate.bind(this));
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
this._handleRoomDelete.bind(this));
},
/**
@ -230,6 +232,8 @@ loop.store.ActiveRoomStore = (function() {
this._mozLoop.rooms.on("update:" + actionData.roomToken,
this._handleRoomUpdate.bind(this));
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
this._handleRoomDelete.bind(this));
},
/**
@ -259,6 +263,18 @@ loop.store.ActiveRoomStore = (function() {
}));
},
/**
* Handles the deletion of a room, notified by the mozLoop rooms API.
*
* @param {String} eventName The name of the event
* @param {Object} roomData The roomData of the deleted room
*/
_handleRoomDelete: function(eventName, roomData) {
this._sdkDriver.forceDisconnectAll(function() {
window.close();
});
},
/**
* Handles the action to join to a room.
*/
@ -391,7 +407,9 @@ loop.store.ActiveRoomStore = (function() {
this._leaveRoom(ROOM_STATES.CLOSING);
// If we're closing the window, we can stop listening to updates.
this._mozLoop.rooms.off("update:" + this.getStoreState().roomToken);
var roomToken = this.getStoreState().roomToken;
this._mozLoop.rooms.off("update:" + roomToken);
this._mozLoop.rooms.off("delete:" + roomToken);
},
/**

View File

@ -25,6 +25,8 @@ loop.OTSdkDriver = (function() {
this.dispatcher = options.dispatcher;
this.sdk = options.sdk;
this.connections = {};
this.dispatcher.register(this, [
"setupStreamElements",
"setMute"
@ -115,6 +117,38 @@ loop.OTSdkDriver = (function() {
delete this._publisherReady;
delete this._publishedLocalStream;
delete this._subscribedRemoteStream;
this.connections = {};
},
/**
* Oust all users from an ongoing session. This is typically done when a room
* owner deletes the room.
*
* @param {Function} callback Function to be invoked once all connections are
* ousted
*/
forceDisconnectAll: function(callback) {
if (!this._sessionConnected) {
callback();
return;
}
var connectionNames = Object.keys(this.connections);
if (connectionNames.length === 0) {
callback();
return;
}
var disconnectCount = 0;
connectionNames.forEach(function(id) {
var connection = this.connections[id];
this.session.forceDisconnect(connection, function() {
// When all connections have disconnected, call the callback, since
// we're done.
if (++disconnectCount === connectionNames.length) {
callback();
}
});
}, this);
},
/**
@ -139,10 +173,14 @@ loop.OTSdkDriver = (function() {
/**
* Handles the connection event for a peer's connection being dropped.
*
* @param {SessionDisconnectEvent} event The event details
* https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
* @param {ConnectionEvent} event The event details
* https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
*/
_onConnectionDestroyed: function(event) {
var connection = event.connection;
if (connection && (connection.id in this.connections)) {
delete this.connections[connection.id];
}
this.dispatcher.dispatch(new sharedActions.RemotePeerDisconnected({
peerHungup: event.reason === "clientDisconnected"
}));
@ -164,11 +202,18 @@ loop.OTSdkDriver = (function() {
}
},
/**
* Handles the connection event for a newly connecting peer.
*
* @param {ConnectionEvent} event The event details
* https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
*/
_onConnectionCreated: function(event) {
if (this.session.connection.id === event.connection.id) {
var connection = event.connection;
if (this.session.connection.id === connection.id) {
return;
}
this.connections[connection.id] = connection;
this.dispatcher.dispatch(new sharedActions.RemotePeerConnected());
},

View File

@ -34,7 +34,8 @@ describe("loop.store.ActiveRoomStore", function () {
fakeSdkDriver = {
connectSession: sandbox.stub(),
disconnectSession: sandbox.stub()
disconnectSession: sandbox.stub(),
forceDisconnectAll: sandbox.stub().callsArg(0)
};
fakeMultiplexGum = {
@ -740,7 +741,7 @@ describe("loop.store.ActiveRoomStore", function () {
});
it("should dispatch an UpdateRoomInfo action", function() {
sinon.assert.calledOnce(fakeMozLoop.rooms.on);
sinon.assert.calledTwice(fakeMozLoop.rooms.on);
var fakeRoomData = {
roomName: "fakeName",
@ -755,5 +756,30 @@ describe("loop.store.ActiveRoomStore", function () {
new sharedActions.UpdateRoomInfo(fakeRoomData));
});
});
describe("delete:{roomToken}", function() {
var fakeRoomData = {
roomName: "Its a room",
roomOwner: "Me",
roomToken: "fakeToken",
roomUrl: "http://invalid"
};
beforeEach(function() {
store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomData));
});
it("should disconnect all room connections", function() {
fakeMozLoop.rooms.on.callArgWith(1, "delete:" + fakeRoomData.roomToken, fakeRoomData);
sinon.assert.calledOnce(fakeSdkDriver.forceDisconnectAll);
});
it("should not disconnect anything when another room is deleted", function() {
fakeMozLoop.rooms.on.callArgWith(1, "delete:invalidToken", fakeRoomData);
sinon.assert.calledOnce(fakeSdkDriver.forceDisconnectAll);
});
});
});
});

View File

@ -34,7 +34,8 @@ describe("loop.OTSdkDriver", function () {
connect: sinon.stub(),
disconnect: sinon.stub(),
publish: sinon.stub(),
subscribe: sinon.stub()
subscribe: sinon.stub(),
forceDisconnect: sinon.stub()
}, Backbone.Events);
publisher = _.extend({
@ -175,6 +176,43 @@ describe("loop.OTSdkDriver", function () {
});
});
describe("#forceDisconnectAll", function() {
it("should not disconnect anything when not connected", function() {
driver.session = session;
driver.forceDisconnectAll(function() {});
sinon.assert.notCalled(session.forceDisconnect);
});
it("should disconnect all remote connections when called", function() {
driver.connectSession(sessionData);
sinon.assert.calledOnce(session.connect);
driver._sessionConnected = true;
// Setup the right state in the driver to make `forceDisconnectAll` do
// something.
session.connection = {
id: "localUser"
};
session.trigger("connectionCreated", {
connection: {id: "remoteUser"}
});
expect(driver.connections).to.include.keys("remoteUser");
driver.forceDisconnectAll(function() {});
sinon.assert.calledOnce(session.forceDisconnect);
// Add another remote connection.
session.trigger("connectionCreated", {
connection: {id: "remoteUser2"}
});
expect(driver.connections).to.include.keys("remoteUser", "remoteUser2");
driver.forceDisconnectAll(function() {});
sinon.assert.calledThrice(session.forceDisconnect);
});
});
describe("Events", function() {
beforeEach(function() {
driver.connectSession(sessionData);
@ -275,6 +313,9 @@ describe("loop.OTSdkDriver", function () {
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.RemotePeerConnected());
it("should store the connection details for a remote user", function() {
expect(driver.connections).to.include.keys("remoteUser");
});
});
it("should not dispatch an action if this is for a local user",
@ -284,6 +325,9 @@ describe("loop.OTSdkDriver", function () {
});
sinon.assert.notCalled(dispatcher.dispatch);
it("should not store the connection details for a local user", function() {
expect(driver.connections).to.not.include.keys("localUser");
});
});
});

View File

@ -18,8 +18,6 @@ devtools.lazyRequireGetter(this, "DevToolsUtils",
"devtools/toolkit/DevToolsUtils");
devtools.lazyRequireGetter(this, "L10N",
"devtools/profiler/global", true);
devtools.lazyRequireGetter(this, "FramerateFront",
"devtools/server/actors/framerate", true);
devtools.lazyRequireGetter(this, "Waterfall",
"devtools/timeline/waterfall", true);
devtools.lazyRequireGetter(this, "MarkerDetails",

View File

@ -3,8 +3,11 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const OVERVIEW_UPDATE_INTERVAL = 100;
const FRAMERATE_CALC_INTERVAL = 16; // ms
// No sense updating the overview more often than receiving data from the
// backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT
// in toolkit/devtools/server/actors/timeline.js
const OVERVIEW_UPDATE_INTERVAL = 200; // ms
const FRAMERATE_GRAPH_HEIGHT = 60; // px
const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms
@ -13,11 +16,10 @@ const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms
* framerate over time.
*/
let OverviewView = {
/**
* Sets up the view with event binding.
*/
initialize: function () {
initialize: Task.async(function *() {
this._framerateEl = $("#time-framerate");
this._ticksData = [];
@ -28,14 +30,14 @@ let OverviewView = {
this._onGraphMouseUp = this._onGraphMouseUp.bind(this);
this._onGraphScroll = this._onGraphScroll.bind(this);
this._initializeFramerateGraph();
yield this._initializeFramerateGraph();
this.framerateGraph.on("mouseup", this._onGraphMouseUp);
this.framerateGraph.on("scroll", this._onGraphScroll);
PerformanceController.on(EVENTS.RECORDING_STARTED, this._start);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._stop);
PerformanceController.on(EVENTS.TIMELINE_DATA, this._onTimelineData);
},
}),
/**
* Unbinds events.
@ -55,9 +57,13 @@ let OverviewView = {
* data into all the corresponding overview graphs.
*/
_onRecordingTick: Task.async(function *() {
yield this.framerateGraph.setDataWhenReady(this._ticksData);
// The `ticks` event on the TimelineFront returns all ticks for the
// recording session, so just convert to plottable values and draw.
let [, timestamps] = this._ticksData;
yield this.framerateGraph.setDataFromTimestamps(timestamps);
this.emit(EVENTS.OVERVIEW_RENDERED);
this._draw();
this._prepareNextTick();
}),
/**
@ -94,20 +100,21 @@ let OverviewView = {
/**
* Sets up the framerate graph.
*/
_initializeFramerateGraph: function () {
_initializeFramerateGraph: Task.async(function *() {
let graph = new LineGraphWidget(this._framerateEl, L10N.getStr("graphs.fps"));
graph.minDistanceBetweenPoints = 1;
graph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
graph.selectionEnabled = false;
this.framerateGraph = graph;
},
yield graph.ready();
}),
/**
* Called to refresh the timer to keep firing _onRecordingTick.
*/
_draw: function () {
_prepareNextTick: function () {
// Check here to see if there's still a _timeoutId, incase
// `stop` was called before the _draw call was executed.
// `stop` was called before the _prepareNextTick call was executed.
if (this._timeoutId) {
this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
}
@ -134,11 +141,7 @@ let OverviewView = {
*/
_onTimelineData: function (_, eventName, ...data) {
if (eventName === "ticks") {
let [delta, timestamps] = data;
// the `ticks` event on the TimelineFront returns all ticks for the
// recording session, so just convert to plottable values
// and store.
this._ticksData = FramerateFront.plotFPS(timestamps, FRAMERATE_CALC_INTERVAL);
this._ticksData = data;
}
}
};

View File

@ -394,7 +394,6 @@ let ProfileView = {
let graph = new LineGraphWidget($(".framerate", panel), L10N.getStr("graphs.fps"));
graph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
graph.minDistanceBetweenPoints = 1;
graph.dataOffsetX = beginAt;
yield graph.setDataWhenReady(framerateData);

View File

@ -23,6 +23,7 @@ EXTRA_JS_MODULES.devtools += [
'widgets/BreadcrumbsWidget.jsm',
'widgets/Chart.jsm',
'widgets/Graphs.jsm',
'widgets/GraphsWorker.js',
'widgets/SideMenuWidget.jsm',
'widgets/SimpleListWidget.jsm',
'widgets/VariablesView.jsm',

View File

@ -19,6 +19,7 @@ this.EXPORTED_SYMBOLS = [
const HTML_NS = "http://www.w3.org/1999/xhtml";
const GRAPH_SRC = "chrome://browser/content/devtools/graphs-frame.xhtml";
const WORKER_URL = "resource:///modules/devtools/GraphsWorker.js";
const L10N = new ViewHelpers.L10N();
// Generic constants.
@ -44,7 +45,7 @@ const GRAPH_STRIPE_PATTERN_LINE_SPACING = 4; // px
// Line graph constants.
const LINE_GRAPH_DAMPEN_VALUES = 0.85;
const LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS = 400; // 20 px
const LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS = 1; // px
const LINE_GRAPH_TOOLTIP_SAFE_BOUNDS = 8; // px
const LINE_GRAPH_MIN_MAX_TOOLTIP_DISTANCE = 14; // px
@ -92,10 +93,10 @@ const BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50; // ms
/**
* Small data primitives for all graphs.
*/
this.GraphCursor = function() {}
this.GraphSelection = function() {}
this.GraphSelectionDragger = function() {}
this.GraphSelectionResizer = function() {}
this.GraphCursor = function() {};
this.GraphSelection = function() {};
this.GraphSelectionDragger = function() {};
this.GraphSelectionResizer = function() {};
GraphCursor.prototype = {
x: null,
@ -206,7 +207,7 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
this._ready.resolve(this);
this.emit("ready", this);
});
}
};
AbstractCanvasGraph.prototype = {
/**
@ -895,7 +896,7 @@ AbstractCanvasGraph.prototype = {
while (node = node.offsetParent) {
x += node.offsetLeft;
y += node.offsetTop;
};
}
return { left: x, top: y };
},
@ -1178,7 +1179,7 @@ this.LineGraphWidget = function(parent, metric, ...args) {
this._avgTooltip = this._createTooltip("average", "end", "avg", metric);
this._minTooltip = this._createTooltip("minimum", "start", "min", metric);
});
}
};
LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
backgroundColor: LINE_GRAPH_BACKGROUND_COLOR,
@ -1211,7 +1212,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
* Points that are too close too each other in the graph will not be rendered.
* This scalar specifies the required minimum squared distance between points.
*/
minDistanceBetweenPoints: LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS,
minSquaredDistanceBetweenPoints: LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS,
/**
* Specifies if min/max/avg tooltips have arrow handlers on their sides.
@ -1224,6 +1225,36 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
*/
withFixedTooltipPositions: false,
/**
* Takes a list of numbers and plots them on a line graph representing
* the rate of occurences in a specified interval. Useful for drawing
* framerate, for example, from a sequence of timestamps.
*
* @param array timestamps
* A list of numbers representing time, ordered ascending. For example,
* this can be the raw data received from the framerate actor, which
* represents the elapsed time on each refresh driver tick.
* @param number interval
* The maximum amount of time to wait between calculations.
*/
setDataFromTimestamps: Task.async(function*(timestamps, interval) {
let {
plottedData,
plottedMinMaxSum
} = yield CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
width: this._width,
height: this._height,
dataOffsetX: this.dataOffsetX,
dampenValuesFactor: this.dampenValuesFactor,
minSquaredDistanceBetweenPoints: this.minSquaredDistanceBetweenPoints,
timestamps: timestamps,
interval: interval
});
this._tempMinMaxSum = plottedMinMaxSum;
this.setData(plottedData);
}),
/**
* Renders the graph's data source.
* @see AbstractCanvasGraph.prototype.buildGraphImage
@ -1238,31 +1269,37 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;
let maxValue = Number.MIN_SAFE_INTEGER;
let minValue = Number.MAX_SAFE_INTEGER;
let sumValues = 0;
let avgValue = 0;
let forceDrawAllPoints = false;
if (this._tempMinMaxSum) {
maxValue = this._tempMinMaxSum.maxValue;
minValue = this._tempMinMaxSum.minValue;
avgValue = this._tempMinMaxSum.avgValue;
// If we use cached `minValue`, `maxValue`, `avgValue` then we can assume
// that we've already removed points that did not meet the
// `minSquaredDistanceBetweenPoints` requirement.
forceDrawAllPoints = true;
} else {
let sumValues = 0;
for (let { delta, value } of this._data) {
maxValue = Math.max(value, maxValue);
minValue = Math.min(value, minValue);
sumValues += value;
}
avgValue = sumValues / totalTicks;
}
let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
let dataScaleY = this.dataScaleY = height / maxValue * this.dampenValuesFactor;
/**
* Calculates the squared distance between two 2D points.
*/
function distSquared(x0, y0, x1, y1) {
let xs = x1 - x0;
let ys = y1 - y0;
return xs * xs + ys * ys;
}
// Draw the graph.
// Draw the background.
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, width, height);
// Draw the graph.
let gradient = ctx.createLinearGradient(0, height / 2, 0, height);
gradient.addColorStop(0, this.backgroundGradientStart);
gradient.addColorStop(1, this.backgroundGradientEnd);
@ -1273,6 +1310,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
let prevX = 0;
let prevY = 0;
let minSqDist = this.minSquaredDistanceBetweenPoints;
for (let { delta, value } of this._data) {
let currX = (delta - this.dataOffsetX) * dataScaleX;
@ -1283,8 +1321,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
ctx.lineTo(-LINE_GRAPH_STROKE_WIDTH, currY);
}
let distance = distSquared(prevX, prevY, currX, currY);
if (distance >= this.minDistanceBetweenPoints) {
if (forceDrawAllPoints || distSquared(prevX, prevY, currX, currY) >= minSqDist) {
ctx.lineTo(currX, currY);
prevX = currX;
prevY = currY;
@ -1299,6 +1336,26 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
ctx.fill();
ctx.stroke();
this._drawOverlays(ctx, minValue, maxValue, avgValue, dataScaleY);
return canvas;
},
/**
* Draws the min, max and average horizontal lines, along with their
* repsective tooltips.
*
* @param CanvasRenderingContext2D ctx
* @param number minValue
* @param number maxValue
* @param number avgValue
* @param number dataScaleY
*/
_drawOverlays: function(ctx, minValue, maxValue, avgValue, dataScaleY) {
let width = this._width;
let height = this._height;
let totalTicks = this._data.length;
// Draw the maximum value horizontal line.
ctx.strokeStyle = this.maximumLineColor;
@ -1316,7 +1373,6 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
ctx.beginPath();
let avgValue = totalTicks ? sumValues / totalTicks : 0;
let averageY = height - avgValue * dataScaleY;
ctx.moveTo(0, averageY);
ctx.lineTo(width, averageY);
@ -1342,15 +1398,6 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
this._minTooltip.querySelector("[text=value]").textContent =
L10N.numberWithDecimals(minValue, 2);
/**
* Constrains a value to a range.
*/
function clamp(value, min, max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
let bottom = height / this._pixelRatio;
let maxPosY = map(maxValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
let avgPosY = map(avgValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
@ -1384,8 +1431,6 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
this._minTooltip.hidden = !totalTicks;
this._gutter.hidden = !this.withTooltipArrows;
return canvas;
},
/**
@ -1445,7 +1490,6 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
}
});
/**
* A bar graph, plotting tuples of values as rectangles.
*
@ -1501,7 +1545,7 @@ this.BarGraphWidget = function(parent, ...args) {
}
this.outstandingEventListeners = null;
});
}
};
BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
clipheadLineColor: BAR_GRAPH_CLIPHEAD_LINE_COLOR,
@ -1955,6 +1999,9 @@ const gCachedStripePattern = new Map();
* Utility functions for graph canvases.
*/
this.CanvasGraphUtils = {
_graphUtilsWorker: null,
_graphUtilsTaskId: 0,
/**
* Merges the animation loop of two graphs.
*/
@ -2004,6 +2051,47 @@ this.CanvasGraphUtils = {
graph2.on("deselecting", () => {
graph1.dropSelection();
});
},
/**
* Performs the given task in a chrome worker, assuming it exists.
*
* @param string task
* The task name. Currently supported: "plotTimestampsGraph".
* @param any args
* Extra arguments to pass to the worker.
* @param array transferrable [optional]
* A list of transferrable objects, if any.
* @return object
* A promise that is resolved once the worker finishes the task.
*/
_performTaskInWorker: function(task, args, transferrable) {
let worker = this._graphUtilsWorker || new ChromeWorker(WORKER_URL);
let id = this._graphUtilsTaskId++;
worker.postMessage({ task, id, args }, transferrable);
return this._waitForWorkerResponse(worker, id);
},
/**
* Waits for the specified worker to finish a task.
*
* @param ChromeWorker worker
* The worker for which to add a message listener.
* @param number id
* The worker task id.
*/
_waitForWorkerResponse: function(worker, id) {
let deferred = promise.defer();
worker.addEventListener("message", function listener({ data }) {
if (data.id != id) {
return;
}
worker.removeEventListener("message", listener);
deferred.resolve(data);
});
return deferred.promise;
}
};
@ -2016,6 +2104,26 @@ function map(value, istart, istop, ostart, ostop) {
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
}
/**
* Constrains a value to a range.
* @param number value, min, max
* @return number
*/
function clamp(value, min, max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
/**
* Calculates the squared distance between two 2D points.
*/
function distSquared(x0, y0, x1, y1) {
let xs = x1 - x0;
let ys = y1 - y0;
return xs * xs + ys * ys;
}
/**
* Finds the first element in an array that validates a predicate.
* @param array

View File

@ -0,0 +1,161 @@
/* 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/. */
"use strict";
self.onmessage = e => {
const { id, task, args } = e.data;
switch (task) {
case "plotTimestampsGraph":
plotTimestampsGraph(id, args);
break;
default:
self.postMessage({ id, error: e.message + "\n" + e.stack });
break;
}
};
/**
* @see LineGraphWidget.prototype.setDataFromTimestamps in Graphs.jsm
* @param number id
* @param number width
* @param number height
* @param array timestamps
* @param number interval
*/
function plotTimestampsGraph(id, args) {
let plottedData = plotTimestamps(args.timestamps, args.interval);
let plottedMinMaxSum = getMinMaxSum(plottedData);
let sparsifiedData = sparsifyLineData(plottedData, plottedMinMaxSum, args);
let response = { id, plottedData: sparsifiedData, plottedMinMaxSum };
self.postMessage(response);
}
/**
* Gets the min, max and average of the values in an array.
* @param array source
* @return object
*/
function getMinMaxSum(source) {
let totalTicks = source.length;
let maxValue = Number.MIN_SAFE_INTEGER;
let minValue = Number.MAX_SAFE_INTEGER;
let avgValue = 0;
let sumValues = 0;
for (let { value } of source) {
maxValue = Math.max(value, maxValue);
minValue = Math.min(value, minValue);
sumValues += value;
}
avgValue = sumValues / totalTicks;
return { minValue, maxValue, avgValue };
}
/**
* Reduce a data source for a line graph, based off of a minimum distance
* between the points to render.
*/
function sparsifyLineData(plottedData, plottedMinMaxSum, options) {
let { width: graphWidth, height: graphHeight } = options;
let { dataOffsetX, dampenValuesFactor } = options;
let { minSquaredDistanceBetweenPoints } = options;
let result = [];
let totalTicks = plottedData.length;
let maxValue = plottedMinMaxSum.maxValue;
let firstTick = totalTicks ? plottedData[0].delta : 0;
let lastTick = totalTicks ? plottedData[totalTicks - 1].delta : 0;
let dataScaleX = graphWidth / (lastTick - dataOffsetX);
let dataScaleY = graphHeight / maxValue * dampenValuesFactor;
let prevX = 0;
let prevY = 0;
for (let { delta, value } of plottedData) {
let currX = (delta - dataOffsetX) * dataScaleX;
let currY = graphHeight - value * dataScaleY;
if (delta == firstTick || delta == lastTick) {
result.push({ delta, value });
continue;
}
let dist = distSquared(prevX, prevY, currX, currY);
if (dist >= minSquaredDistanceBetweenPoints) {
result.push({ delta, value });
prevX = currX;
prevY = currY;
}
}
return result;
}
/**
* Takes a list of numbers and plots them on a line graph representing
* the rate of occurences in a specified interval.
*
* XXX: Copied almost verbatim from toolkit/devtools/server/actors/framerate.js
* Remove that dead code after the Performance panel lands, bug 1075567.
*
* @param array timestamps
* A list of numbers representing time, ordered ascending. For example,
* this can be the raw data received from the framerate actor, which
* represents the elapsed time on each refresh driver tick.
* @param number interval
* The maximum amount of time to wait between calculations.
* @param number clamp
* The maximum allowed value.
* @return array
* A collection of { delta, value } objects representing the
* plotted value at every delta time.
*/
function plotTimestamps(timestamps, interval = 100, clamp = 60) {
let timeline = [];
let totalTicks = timestamps.length;
// If the refresh driver didn't get a chance to tick before the
// recording was stopped, assume rate was 0.
if (totalTicks == 0) {
timeline.push({ delta: 0, value: 0 });
timeline.push({ delta: interval, value: 0 });
return timeline;
}
let frameCount = 0;
let prevTime = +timestamps[0];
for (let i = 1; i < totalTicks; i++) {
let currTime = +timestamps[i];
frameCount++;
let elapsedTime = currTime - prevTime;
if (elapsedTime < interval) {
continue;
}
let rate = Math.min(1000 / (elapsedTime / frameCount), clamp);
timeline.push({ delta: prevTime, value: rate });
timeline.push({ delta: currTime, value: rate });
frameCount = 0;
prevTime = currTime;
}
return timeline;
}
/**
* Calculates the squared distance between two 2D points.
*/
function distSquared(x0, y0, x1, y1) {
let xs = x1 - x0;
let ys = y1 - y0;
return xs * xs + ys * ys;
}

View File

@ -3,20 +3,26 @@
* 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/. */
/* CSS Variables specific to this panel that aren't defined by the themes */
.theme-dark {
--cell-border-color: rgba(255,255,255,0.15);
--focus-cell-border-color: rgba(255,255,255,0.5);
--row-alt-background-color: rgba(29,79,115,0.15);
--row-hover-background-color: rgba(29,79,115,0.25);
}
.theme-light {
--cell-border-color: rgba(0,0,0,0.15);
--focus-cell-border-color: rgba(0,0,0,0.3);
--row-alt-background-color: rgba(76,158,217,0.1);
--row-hover-background-color: rgba(76,158,217,0.2);
}
/* Toolbar */
#performance-toolbar > tabs,
#performance-toolbar {
-moz-border-end: 1px solid;
}
.theme-dark #performance-toolbar > tabs,
.theme-dark #performance-toolbar {
-moz-border-end-color: #000; /* Splitters */
}
.theme-light #performance-toolbar > tabs,
.theme-light #performance-toolbar {
-moz-border-end-color: #aaa; /* Splitters */
-moz-border-end-color: var(--theme-splitter-color);
}
/* Overview Panel */
@ -85,23 +91,14 @@
-moz-box-align: center;
overflow: hidden;
padding: 1px 4px;
color: var(--theme-body-color);
-moz-border-end-color: var(--cell-border-color);
}
.call-tree-header:not(:last-child),
.call-tree-cell:not(:last-child) {
-moz-border-end: 1px solid;
}
.theme-dark .call-tree-header,
.theme-dark .call-tree-cell {
-moz-border-end-color: rgba(255,255,255,0.15);
color: #8fa1b2; /* Body Text */
}
.theme-light .call-tree-header,
.theme-light .call-tree-cell {
-moz-border-end-color: rgba(0,0,0,0.15);
color: #18191a; /* Body Text */
-moz-border-end-width: 1px;
-moz-border-end-style: solid;
}
.call-tree-header:not(:last-child) {
@ -112,56 +109,32 @@
text-align: end;
}
.theme-dark .call-tree-header {
background-color: #252c33; /* Tab Toolbar */
.call-tree-header {
background-color: var(--theme-tab-toolbar-background);
}
.theme-light .call-tree-header {
background-color: #ebeced; /* Tab Toolbar */
.call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid var(--cell-border-color);
}
.theme-dark .call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid rgba(255,255,255,0.15);
.call-tree-item:nth-child(2n) {
background-color: var(--row-alt-background-color);
}
.theme-light .call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid rgba(0,0,0,0.15);
.call-tree-item:hover {
background-color: var(--row-hover-background-color);
}
.theme-dark .call-tree-item:nth-child(2n) {
background-color: rgba(29,79,115,0.15);
}
.theme-light .call-tree-item:nth-child(2n) {
background-color: rgba(76,158,217,0.1);
}
.theme-dark .call-tree-item:hover {
background-color: rgba(29,79,115,0.25);
}
.theme-light .call-tree-item:hover {
background-color: rgba(76,158,217,0.2);
}
.theme-dark .call-tree-item:focus {
background-color: #1d4f73; /* Select Highlight Blue */
}
.theme-light .call-tree-item:focus {
background-color: #4c9ed9; /* Select Highlight Blue */
.call-tree-item:focus {
background-color: var(--theme-selection-background);
}
.call-tree-item:focus label {
color: #f5f7fa !important; /* Light foreground text */
color: var(--theme-selection-color) !important;
}
.theme-dark .call-tree-item:focus .call-tree-cell {
-moz-border-end-color: rgba(0,0,0,0.3);
}
.theme-light .call-tree-item:focus .call-tree-cell {
-moz-border-end-color: rgba(255,255,255,0.5);
.call-tree-item:focus .call-tree-cell {
-moz-border-end-color: var(--focus-cell-border-color);
}
.call-tree-item:not([origin="content"]) .call-tree-name,
@ -180,33 +153,18 @@
text-decoration: underline;
}
.theme-dark .call-tree-url {
color: #46afe3;
.call-tree-url {
color: var(--theme-highlight-blue);
}
.theme-light .call-tree-url {
color: #0088cc;
}
.theme-dark .call-tree-line {
color: #d96629;
}
.theme-light .call-tree-line {
color: #f13c00;
.call-tree-line {
color: var(--theme-highlight-orange);
}
.call-tree-host {
-moz-margin-start: 8px !important;
font-size: 90%;
}
.theme-dark .call-tree-host {
color: #8fa1b2;
}
.theme-light .call-tree-host {
color: #8fa1b2;
color: var(--theme-content-color2);
}
.call-tree-url[value=""],
@ -307,15 +265,7 @@
}
.waterfall-sidebar {
-moz-border-end: 1px solid;
}
.theme-dark .waterfall-sidebar {
-moz-border-end-color: #000;
}
.theme-light .waterfall-sidebar {
-moz-border-end-color: #aaa;
-moz-border-end: 1px solid var(--theme-splitter-color);
}
.waterfall-marker-container:hover > .waterfall-sidebar {
@ -330,14 +280,7 @@
width: 100px;
font-size: 9px;
transform-origin: left center;
}
.theme-dark .waterfall-header-tick {
color: #a9bacb;
}
.theme-light .waterfall-header-tick {
color: #292e33;
color: var(--theme-body-color);
}
.waterfall-header-tick:not(:first-child) {
@ -365,16 +308,9 @@
transform-origin: left center;
}
.theme-light .waterfall-marker-container.selected > .waterfall-sidebar,
.theme-light .waterfall-marker-container.selected > .waterfall-marker-item {
background-color: #4c9ed9; /* Select Highlight Blue */
color: #f5f7fa; /* Light foreground text */
}
.theme-dark .waterfall-marker-container.selected > .waterfall-sidebar,
.theme-dark .waterfall-marker-container.selected > .waterfall-marker-item {
background-color: #1d4f73; /* Select Highlight Blue */
color: #f5f7fa; /* Light foreground text */
.waterfall-marker-container.selected > .waterfall-marker-item {
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
}
.waterfall-marker-container.selected .waterfall-marker-bullet,

View File

@ -3,6 +3,21 @@
* 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/. */
/* CSS Variables specific to this panel that aren't defined by the themes */
.theme-dark {
--cell-border-color: rgba(255,255,255,0.15);
--focus-cell-border-color: rgba(0,0,0,0.3);
--row-alt-background-color: rgba(29,79,115,0.15);
--row-hover-background-color: rgba(29,79,115,0.25);
}
.theme-light {
--cell-border-color: rgba(0,0,0,0.15);
--focus-cell-border-color: rgba(255,255,255,0.5);
--row-alt-background-color: rgba(76,158,217,0.1);
--row-hover-background-color: rgba(76,158,217,0.2);
}
/* Reload and waiting notices */
.notice-container {
@ -245,17 +260,13 @@
.call-tree-header:not(:last-child),
.call-tree-cell:not(:last-child) {
-moz-border-end: 1px solid;
-moz-border-end-width: 1px;
-moz-border-end-style: solid;
}
.theme-dark .call-tree-header,
.theme-dark .call-tree-cell {
-moz-border-end-color: rgba(255,255,255,0.15);
}
.theme-light .call-tree-header,
.theme-light .call-tree-cell {
-moz-border-end-color: rgba(0,0,0,0.15);
.call-tree-header,
.call-tree-cell {
-moz-border-end-color: var(--cell-border-color);
}
.call-tree-header:not(:last-child) {
@ -270,28 +281,16 @@
background-color: var(--theme-tab-toolbar-background);
}
.theme-dark .call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid rgba(255,255,255,0.15);
.call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid var(--cell-border-color);
}
.theme-light .call-tree-item:last-child:not(:focus) {
border-bottom: 1px solid rgba(0,0,0,0.15);
.call-tree-item:nth-child(2n) {
background-color: var(--row-alt-background-color);
}
.theme-dark .call-tree-item:nth-child(2n) {
background-color: rgba(29,79,115,0.15);
}
.theme-light .call-tree-item:nth-child(2n) {
background-color: rgba(76,158,217,0.1);
}
.theme-dark .call-tree-item:hover {
background-color: rgba(29,79,115,0.25);
}
.theme-light .call-tree-item:hover {
background-color: rgba(76,158,217,0.2);
.call-tree-item:hover {
background-color: var(--row-hover-background-color);
}
.call-tree-item:focus {
@ -302,12 +301,8 @@
color: var(--theme-selection-color) !important;
}
.theme-dark .call-tree-item:focus .call-tree-cell {
-moz-border-end-color: rgba(0,0,0,0.3);
}
.theme-light .call-tree-item:focus .call-tree-cell {
-moz-border-end-color: rgba(255,255,255,0.5);
.call-tree-item:focus .call-tree-cell {
-moz-border-end-color: var(--focus-cell-border-color);
}
.call-tree-item:not([origin="content"]) .call-tree-name,

View File

@ -77,8 +77,13 @@ AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
#endif
CHECK(aObserver);
#if ANDROID_VERSION >= 21
mSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mSessionId, -1);
#else
mSessionId = AudioSystem::newAudioSessionId();
AudioSystem::acquireAudioSessionId(mSessionId);
#endif
mAudioSink = new AudioOutput(mSessionId,
IPCThreadState::self()->getCallingUid());
}
@ -86,7 +91,11 @@ AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
AudioOffloadPlayer::~AudioOffloadPlayer()
{
Reset();
#if ANDROID_VERSION >= 21
AudioSystem::releaseAudioSessionId(mSessionId, -1);
#else
AudioSystem::releaseAudioSessionId(mSessionId);
#endif
}
void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource)

View File

@ -9,10 +9,13 @@ interface MozWakeLockListener;
* The reason for the factory reset.
* "normal" : simple factory reset.
* "wipe" : will also attempt to wipe all user storage areas.
* "root" : simple factory reset that also root the phone to get more
* privileges when using devtools.
*/
enum FactoryResetReason {
"normal",
"wipe"
"wipe",
"root"
};
/**

View File

@ -1777,6 +1777,8 @@ FactoryReset(FactoryResetReason& aReason)
if (aReason == FactoryResetReason::Wipe) {
recoveryService->FactoryReset("wipe");
} else if (aReason == FactoryResetReason::Root) {
recoveryService->FactoryReset("root");
} else {
recoveryService->FactoryReset("normal");
}

View File

@ -446,6 +446,8 @@ FactoryReset(FactoryResetReason& aReason)
Hal()->SendFactoryReset(NS_LITERAL_STRING("normal"));
} else if (aReason == FactoryResetReason::Wipe) {
Hal()->SendFactoryReset(NS_LITERAL_STRING("wipe"));
} else if (aReason == FactoryResetReason::Root) {
Hal()->SendFactoryReset(NS_LITERAL_STRING("root"));
}
}
@ -870,6 +872,8 @@ public:
reason = FactoryResetReason::Normal;
} else if (aReason.EqualsLiteral("wipe")) {
reason = FactoryResetReason::Wipe;
} else if (aReason.EqualsLiteral("root")) {
reason = FactoryResetReason::Root;
} else {
// Invalid factory reset reason. That should never happen.
return false;

View File

@ -65,7 +65,12 @@ public abstract class SessionParser {
for (int i = 0; i < tabs.length(); i++) {
final JSONObject tab = tabs.getJSONObject(i);
final int index = tab.getInt("index");
final JSONObject entry = tab.getJSONArray("entries").getJSONObject(index - 1);
final JSONArray entries = tab.getJSONArray("entries");
if (index < 1 || entries.length() < index) {
Log.w(LOGTAG, "Session entries and index don't agree.");
continue;
}
final JSONObject entry = entries.getJSONObject(index - 1);
final String url = entry.getString("url");
String title = entry.optString("title");

View File

@ -244,6 +244,7 @@ SandboxFilterImplContent::Build() {
Allow(SYSCALL(sched_setparam));
Allow(SYSCALL(sigaltstack));
Allow(SYSCALL(pipe));
Allow(SYSCALL(set_tid_address));
/* Always last and always OK calls */
/* Architecture-specific very infrequently used syscalls */

View File

@ -34,6 +34,19 @@ const {FramerateActor} = require("devtools/server/actors/framerate");
// docShell, no event is sent).
const DEFAULT_TIMELINE_DATA_PULL_TIMEOUT = 200; // ms
/**
* Type representing an array of numbers as strings, serialized fast(er).
* http://jsperf.com/json-stringify-parse-vs-array-join-split/3
*
* XXX: It would be nice if on local connections (only), we could just *give*
* the array directly to the front, instead of going through all this
* serialization redundancy.
*/
protocol.types.addType("array-of-numbers-as-strings", {
write: (v) => v.join(","),
read: (v) => v.split(",")
});
/**
* The timeline actor pops and forwards timeline markers registered in docshells.
*/
@ -72,7 +85,7 @@ let TimelineActor = exports.TimelineActor = protocol.ActorClass({
"ticks" : {
type: "ticks",
delta: Arg(0, "number"),
timestamps: Arg(1, "array:number")
timestamps: Arg(1, "array-of-numbers-as-strings")
}
},