Merge inbound to mozilla-central. a=merge

This commit is contained in:
shindli 2019-02-01 05:56:53 +02:00
Родитель f68a0b40f7 597f660d81
Коммит d6dd350c66
46 изменённых файлов: 694 добавлений и 249 удалений

Просмотреть файл

@ -187,15 +187,6 @@ function removeXHRBreakpoint(path: string, method: string) {
return threadClient.removeXHRBreakpoint(path, method);
}
// Source and breakpoint clients do not yet support an options structure, so
// for now we transform options into condition strings when setting breakpoints.
function transformOptionsToCondition(options) {
if (options.logValue) {
return `console.log(${options.logValue})`;
}
return options.condition;
}
function setBreakpoint(
location: SourceActorLocation,
options: BreakpointOptions,
@ -210,7 +201,7 @@ function setBreakpoint(
.setBreakpoint({
line: location.line,
column: location.column,
condition: transformOptionsToCondition(options),
options,
noSliding
})
.then(([{ actualLocation }, bpClient]) => {
@ -248,14 +239,18 @@ function setBreakpointOptions(
) {
const id = makeBreakpointActorId(location);
const bpClient = bpClients[id];
delete bpClients[id];
const sourceThreadClient = bpClient.source._activeThread;
return bpClient
.setCondition(sourceThreadClient, transformOptionsToCondition(options))
.then(_bpClient => {
bpClients[id] = _bpClient;
});
if (debuggerClient.mainRoot.traits.nativeLogpoints) {
bpClient.setOptions(options);
} else {
// Older server breakpoints destroy themselves when changing options.
delete bpClients[id];
bpClient
.setOptions(options)
.then(_bpClient => {
bpClients[id] = _bpClient;
});
}
}
async function evaluateInFrame(script: Script, options: EvaluateParam) {

Просмотреть файл

@ -398,7 +398,7 @@ export type BreakpointClient = {
line: number,
column: ?number
},
setCondition: (ThreadClient, ?string) => Promise<BreakpointClient>,
setOptions: (BreakpointOptions) => Promise<BreakpointClient>,
// request: any,
source: SourceClient,
options: BreakpointOptions

Просмотреть файл

@ -72,8 +72,9 @@ add_task(async function() {
assertEditorBreakpoint(dbg, 5, true);
// Edit the conditional breakpoint set above
const bpCondition1 = waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
await setConditionalBreakpoint(dbg, 5, "2");
await waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
await bpCondition1;
bp = findBreakpoint(dbg, "simple2", 5);
is(bp.options.condition, "12", "breakpoint is created with the condition");
assertEditorBreakpoint(dbg, 5, true);
@ -87,19 +88,20 @@ add_task(async function() {
// Adding a condition to a breakpoint
clickElement(dbg, "gutter", 5);
await waitForDispatch(dbg, "ADD_BREAKPOINT");
const bpCondition2 = waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
await setConditionalBreakpoint(dbg, 5, "1");
await waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
await bpCondition2;
bp = findBreakpoint(dbg, "simple2", 5);
is(bp.options.condition, "1", "breakpoint is created with the condition");
assertEditorBreakpoint(dbg, 5, true);
const bpCondition = waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
const bpCondition3 = waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
//right click breakpoint in breakpoints list
rightClickElement(dbg, "breakpointItem", 3)
// select "remove condition";
selectContextMenuItem(dbg, selectors.breakpointContextMenu.removeCondition);
await bpCondition;
await bpCondition3;
bp = findBreakpoint(dbg, "simple2", 5);
is(bp.options.condition, undefined, "breakpoint condition removed");
});

Просмотреть файл

@ -468,6 +468,12 @@ Toolbox.prototype = {
this._threadClient = await attachThread(this);
await domReady;
// The web console is immediately loaded when replaying, so that the
// timeline will always be populated with generated messages.
if (this.target.isReplayEnabled()) {
await this.loadTool("webconsole");
}
this.isReady = true;
const framesPromise = this._listFrames();

Просмотреть файл

@ -30,6 +30,7 @@ function WebConsoleConnectionProxy(webConsoleFrame, target) {
this._onPageError = this._onPageError.bind(this);
this._onLogMessage = this._onLogMessage.bind(this);
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
this._onVirtualConsoleLog = this._onVirtualConsoleLog.bind(this);
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onTabNavigated = this._onTabNavigated.bind(this);
@ -127,6 +128,8 @@ WebConsoleConnectionProxy.prototype = {
client.addListener("consoleAPICall", this._onConsoleAPICall);
client.addListener("lastPrivateContextExited",
this._onLastPrivateContextExited);
client.addListener("virtualConsoleLog",
this._onVirtualConsoleLog);
this.target.on("will-navigate", this._onTabWillNavigate);
this.target.on("navigate", this._onTabNavigated);
@ -310,6 +313,20 @@ WebConsoleConnectionProxy.prototype = {
}
this.dispatchMessageAdd(packet);
},
_onVirtualConsoleLog: function(type, packet) {
if (!this.webConsoleFrame) {
return;
}
this.dispatchMessageAdd({
type: "consoleAPICall",
message: {
executionPoint: packet.executionPoint,
"arguments": [packet.url + ":" + packet.line, packet.message],
},
});
},
/**
* The "networkEvent" message type handler. We redirect any message to
* the UI for displaying.
@ -423,6 +440,8 @@ WebConsoleConnectionProxy.prototype = {
this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
this.client.removeListener("lastPrivateContextExited",
this._onLastPrivateContextExited);
this.client.removeListener("virtualConsoleLog",
this._onVirtualConsoleLog);
this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
this.target.off("will-navigate", this._onTabWillNavigate);

Просмотреть файл

@ -22,10 +22,7 @@ const { breakpointSpec } = require("devtools/shared/specs/breakpoint");
*/
function setBreakpointAtEntryPoints(actor, entryPoints) {
for (const { script, offsets } of entryPoints) {
actor.addScript(script);
for (const offset of offsets) {
script.setBreakpoint(offset, actor);
}
actor.addScript(script, offsets);
}
}
@ -46,16 +43,25 @@ const BreakpointActor = ActorClassWithSpec(breakpointSpec, {
* The generated location of the breakpoint.
*/
initialize: function(threadActor, generatedLocation) {
// The set of Debugger.Script instances that this breakpoint has been set
// upon.
this.scripts = new Set();
// A map from Debugger.Script instances to the offsets which the breakpoint
// has been set for in that script.
this.scripts = new Map();
this.threadActor = threadActor;
this.generatedLocation = generatedLocation;
this.condition = null;
this.options = null;
this.isPending = true;
},
// Called when new breakpoint options are received from the client.
setOptions(options) {
for (const [script, offsets] of this.scripts) {
this._updateOptionsForScript(script, offsets, this.options, options);
}
this.options = options;
},
destroy: function() {
this.removeScripts();
},
@ -70,22 +76,58 @@ const BreakpointActor = ActorClassWithSpec(breakpointSpec, {
*
* @param script Debugger.Script
* The new source script on which the breakpoint has been set.
* @param offsets Array
* Any offsets in the script the breakpoint is associated with.
*/
addScript: function(script) {
this.scripts.add(script);
addScript: function(script, offsets) {
this.scripts.set(script, offsets.concat(this.scripts.get(offsets) || []));
for (const offset of offsets) {
script.setBreakpoint(offset, this);
}
this.isPending = false;
this._updateOptionsForScript(script, offsets, null, this.options);
},
/**
* Remove the breakpoints from associated scripts and clear the script cache.
*/
removeScripts: function() {
for (const script of this.scripts) {
for (const [script, offsets] of this.scripts) {
this._updateOptionsForScript(script, offsets, this.options, null);
script.clearBreakpoint(this);
}
this.scripts.clear();
},
// Update any state affected by changing options on a script this breakpoint
// is associated with.
_updateOptionsForScript(script, offsets, oldOptions, newOptions) {
if (this.threadActor.dbg.replaying) {
// When replaying, logging breakpoints are handled using an API to get logged
// messages from throughout the recording.
const oldLogValue = oldOptions && oldOptions.logValue;
const newLogValue = newOptions && newOptions.logValue;
if (oldLogValue != newLogValue) {
for (const offset of offsets) {
const { lineNumber, columnNumber } = script.getOffsetLocation(offset);
script.replayVirtualConsoleLog(offset, newLogValue, (point, rv) => {
const packet = {
from: this.actorID,
type: "virtualConsoleLog",
url: script.url,
line: lineNumber,
column: columnNumber,
executionPoint: point,
message: "return" in rv ? "" + rv.return : "" + rv.throw,
};
this.conn.send(packet);
});
}
}
}
},
/**
* Check if this breakpoint has a condition that doesn't error and
* evaluates to true in frame.
@ -100,8 +142,8 @@ const BreakpointActor = ActorClassWithSpec(breakpointSpec, {
* - message: string
* If the condition throws, this is the thrown message.
*/
checkCondition: function(frame) {
const completion = frame.eval(this.condition);
checkCondition: function(frame, condition) {
const completion = frame.eval(condition);
if (completion) {
if (completion.throw) {
// The evaluation failed and threw
@ -162,15 +204,29 @@ const BreakpointActor = ActorClassWithSpec(breakpointSpec, {
}
const reason = {};
const { condition, logValue } = this.options || {};
if (this.threadActor._hiddenBreakpoints.has(this.actorID)) {
reason.type = "pauseOnDOMEvents";
} else if (!this.condition) {
} else if (!condition && !logValue) {
reason.type = "breakpoint";
// TODO: add the rest of the breakpoints on that line (bug 676602).
reason.actors = [ this.actorID ];
} else {
const { result, message } = this.checkCondition(frame);
// When replaying, breakpoints with log values are handled separately.
if (logValue && this.threadActor.dbg.replaying) {
return undefined;
}
let condstr = condition;
if (logValue) {
// In the non-replaying case, log values are handled by treating them as
// conditions. console.log() never returns true so we will not pause.
condstr = condition
? `(${condition}) && console.log(${logValue})`
: `console.log(${logValue})`;
}
const { result, message } = this.checkCondition(frame, condstr);
if (result) {
if (!message) {

Просмотреть файл

@ -691,6 +691,9 @@ function flushRecording() {
}
}
// After flushing the recording there may be more search results.
maybeResumeSearch();
gLastRecordingCheckpoint = gActiveChild.lastCheckpoint();
// We now have a usable recording for replaying children.
@ -988,16 +991,92 @@ const gControl = {
timeWarp,
};
// eslint-disable-next-line no-unused-vars
function ConnectDebugger(dbg) {
gDebugger = dbg;
dbg._control = gControl;
////////////////////////////////////////////////////////////////////////////////
// Search Operations
////////////////////////////////////////////////////////////////////////////////
let gSearchChild;
let gSearchRestartNeeded;
function maybeRestartSearch() {
if (gSearchRestartNeeded && gSearchChild.paused) {
if (gSearchChild.lastPausePoint.checkpoint != FirstCheckpointId ||
gSearchChild.lastPausePoint.position) {
gSearchChild.sendRestoreCheckpoint(FirstCheckpointId);
gSearchChild.waitUntilPaused();
}
gSearchChild.sendClearBreakpoints();
gDebugger._forEachSearch(pos => gSearchChild.sendAddBreakpoint(pos));
gSearchRestartNeeded = false;
gSearchChild.sendResume({ forward: true });
return true;
}
return false;
}
function ChildRoleSearch() {}
ChildRoleSearch.prototype = {
name: "Search",
initialize(child, { startup }) {
this.child = child;
},
hitExecutionPoint({ point, recordingEndpoint }) {
if (maybeRestartSearch()) {
return;
}
if (point.position) {
gDebugger._onSearchPause(point);
}
if (!recordingEndpoint) {
this.poke();
}
},
poke() {
if (!gSearchRestartNeeded && !this.child.pauseNeeded) {
this.child.sendResume({ forward: true });
}
},
};
function ensureHasSearchChild() {
if (!gSearchChild) {
gSearchChild = spawnReplayingChild(new ChildRoleSearch());
}
}
function maybeResumeSearch() {
if (gSearchChild && gSearchChild.paused) {
gSearchChild.sendResume({ forward: true });
}
}
const gSearchControl = {
reset() {
ensureHasSearchChild();
gSearchRestartNeeded = true;
maybeRestartSearch();
},
sendRequest(request) { return gSearchChild.sendDebuggerRequest(request); },
};
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
// eslint-disable-next-line no-unused-vars
function ConnectDebugger(dbg) {
gDebugger = dbg;
dbg._control = gControl;
dbg._searchControl = gSearchControl;
}
function dumpv(str) {
//dump("[ReplayControl] " + str + "\n");
}

Просмотреть файл

@ -42,6 +42,7 @@ function ReplayDebugger() {
// We should have been connected to control.js by the call above.
assert(this._control);
assert(this._searchControl);
// Preferred direction of travel when not explicitly resumed.
this._direction = Direction.NONE;
@ -75,6 +76,9 @@ function ReplayDebugger() {
// After we are done pausing, callback describing how to resume.
this._resumeCallback = null;
// Information about all searches that exist.
this._searches = [];
// Handler called when hitting the beginning/end of the recording, or when
// a time warp target has been reached.
this.replayingOnForcedPause = null;
@ -348,6 +352,47 @@ ReplayDebugger.prototype = {
this._objects.length = 0;
},
/////////////////////////////////////////////////////////
// Search management
/////////////////////////////////////////////////////////
_forEachSearch(callback) {
for (const { position } of this._searches) {
callback(position);
}
},
_virtualConsoleLog(position, text, callback) {
this._searches.push({ position, text, callback, results: [] });
this._searchControl.reset();
},
_onSearchPause(point) {
for (const { position, text, callback, results } of this._searches) {
if (RecordReplayControl.positionSubsumes(position, point.position)) {
if (!results.some(existing => point.progress == existing.progress)) {
let evaluateResult;
if (text) {
const frameData = this._searchControl.sendRequest({
type: "getFrame",
index: NewestFrameIndex,
});
if ("index" in frameData) {
const rv = this._searchControl.sendRequest({
type: "frameEvaluate",
index: frameData.index,
text,
});
evaluateResult = this._convertCompletionValue(rv, { forSearch: true });
}
}
results.push(point);
callback(point, evaluateResult);
}
}
}
},
/////////////////////////////////////////////////////////
// Breakpoint management
/////////////////////////////////////////////////////////
@ -485,14 +530,20 @@ ReplayDebugger.prototype = {
// Object methods
/////////////////////////////////////////////////////////
// Objects which |forConsole| is set are objects that were logged in console
// messages, and had their properties recorded so that they can be inspected
// without switching to a replaying child.
_getObject(id, forConsole) {
_getObject(id, options) {
if (options && options.forSearch) {
// Returning objects through searches is NYI.
return "<UnknownSearchObject>";
}
const forConsole = options && options.forConsole;
if (id && !this._objects[id]) {
const data = this._sendRequest({ type: "getObject", id });
switch (data.kind) {
case "Object":
// Objects which |forConsole| is set are objects that were logged in
// console messages, and had their properties recorded so that they can
// be inspected without switching to a replaying child.
this._objects[id] = new ReplayDebuggerObject(this, data, forConsole);
break;
case "Environment":
@ -509,10 +560,10 @@ ReplayDebugger.prototype = {
return rv;
},
_convertValue(value, forConsole) {
_convertValue(value, options) {
if (isNonNullObject(value)) {
if (value.object) {
return this._getObject(value.object, forConsole);
return this._getObject(value.object, options);
} else if (value.special == "undefined") {
return undefined;
} else if (value.special == "NaN") {
@ -526,12 +577,12 @@ ReplayDebugger.prototype = {
return value;
},
_convertCompletionValue(value) {
_convertCompletionValue(value, options) {
if ("return" in value) {
return { return: this._convertValue(value.return) };
return { return: this._convertValue(value.return, options) };
}
if ("throw" in value) {
return { throw: this._convertValue(value.throw) };
return { throw: this._convertValue(value.throw, options) };
}
ThrowError("Unexpected completion value");
return null; // For eslint
@ -582,7 +633,7 @@ ReplayDebugger.prototype = {
if (message.messageType == "ConsoleAPI" && message.arguments) {
for (let i = 0; i < message.arguments.length; i++) {
message.arguments[i] = this._convertValue(message.arguments[i],
/* forConsole = */ true);
{ forConsole: true });
}
}
return message;
@ -662,6 +713,7 @@ ReplayDebuggerScript.prototype = {
get format() { return this._data.format; },
_forward(type, value) {
this._dbg._ensurePaused();
return this._dbg._sendRequest({ type, id: this._data.id, value });
},
@ -683,6 +735,11 @@ ReplayDebuggerScript.prototype = {
});
},
replayVirtualConsoleLog(offset, text, callback) {
this._dbg._virtualConsoleLog({ kind: "Break", script: this._data.id, offset },
text, callback);
},
get isGeneratorFunction() { NYI(); },
get isAsyncFunction() { NYI(); },
getChildScripts: NYI,

Просмотреть файл

@ -170,6 +170,9 @@ RootActor.prototype = {
// `front.startProfiler`. This is an optional parameter but it will throw an error if
// the profiled Firefox doesn't accept it.
perfActorVersion: 1,
// Supports native log points and modifying the condition/log of an existing
// breakpoints. Fx66+
nativeLogpoints: true,
},
/**

Просмотреть файл

@ -559,8 +559,8 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
* Line to break on.
* @param Number column
* Column to break on.
* @param String condition
* A condition which must be true for breakpoint to be hit.
* @param Object options
* Any options for the breakpoint.
* @param Boolean noSliding
* If true, disables breakpoint sliding.
*
@ -568,11 +568,11 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
* A promise that resolves to a JSON object representing the
* response.
*/
setBreakpoint: function(line, column, condition, noSliding) {
setBreakpoint: function(line, column, options, noSliding) {
const location = new GeneratedLocation(this, line, column);
const actor = this._getOrCreateBreakpointActor(
location,
condition,
options,
noSliding
);
@ -597,16 +597,15 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
* @param GeneratedLocation generatedLocation
* A GeneratedLocation representing the location of the breakpoint in
* the generated source.
* @param String condition
* A string that is evaluated whenever the breakpoint is hit. If the
* string evaluates to false, the breakpoint is ignored.
* @param Object options
* Any options for the breakpoint.
* @param Boolean noSliding
* If true, disables breakpoint sliding.
*
* @returns BreakpointActor
* A BreakpointActor representing the breakpoint.
*/
_getOrCreateBreakpointActor: function(generatedLocation, condition, noSliding) {
_getOrCreateBreakpointActor: function(generatedLocation, options, noSliding) {
let actor = this.breakpointActorMap.getActor(generatedLocation);
if (!actor) {
actor = new BreakpointActor(this.threadActor, generatedLocation);
@ -614,7 +613,7 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
this.breakpointActorMap.setActor(generatedLocation, actor);
}
actor.condition = condition;
actor.setOptions(options);
return this._setBreakpoint(actor, noSliding);
},

Просмотреть файл

@ -27,6 +27,8 @@ function run_test() {
}
function test_simple_breakpoint() {
let hitBreakpoint = false;
gThreadClient.addOneTimeListener("paused", async function(event, packet) {
const source = await getSourceById(
gThreadClient,
@ -34,9 +36,12 @@ function test_simple_breakpoint() {
);
source.setBreakpoint({
line: 3,
condition: "a === 1",
options: { condition: "a === 1" },
}).then(function([response, bpClient]) {
gThreadClient.addOneTimeListener("paused", function(event, packet) {
Assert.equal(hitBreakpoint, false);
hitBreakpoint = true;
// Check the return value.
Assert.equal(packet.why.type, "breakpoint");
Assert.equal(packet.frame.where.line, 3);
@ -62,4 +67,6 @@ function test_simple_breakpoint() {
"test.js",
1);
/* eslint-enable */
Assert.equal(hitBreakpoint, true);
}

Просмотреть файл

@ -32,13 +32,17 @@ function test_simple_breakpoint() {
gThreadClient,
packet.frame.where.actor
);
source.setBreakpoint({
await source.setBreakpoint({
line: 3,
condition: "a === 2",
options: { condition: "a === 2" },
});
source.setBreakpoint({
line: 4,
options: { condition: "a === 1" },
}).then(function([response, bpClient]) {
gThreadClient.addOneTimeListener("paused", function(event, packet) {
// Check the return value.
Assert.equal(packet.why.type, "debuggerStatement");
Assert.equal(packet.why.type, "breakpoint");
Assert.equal(packet.frame.where.line, 4);
// Remove the breakpoint.
@ -57,7 +61,8 @@ function test_simple_breakpoint() {
Cu.evalInSandbox("debugger;\n" + // 1
"var a = 1;\n" + // 2
"var b = 2;\n" + // 3
"debugger;", // 4
"b++;" + // 4
"debugger;", // 5
gDebuggee,
"1.8",
"test.js",

Просмотреть файл

@ -34,7 +34,7 @@ function test_simple_breakpoint() {
);
source.setBreakpoint({
line: 3,
condition: "throw new Error()",
options: { condition: "throw new Error()" },
}).then(function([response, bpClient]) {
gThreadClient.addOneTimeListener("paused", function(event, packet) {
// Check the return value.

Просмотреть файл

@ -0,0 +1,60 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Check that logpoints call console.log.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-logpoint");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect().then(function() {
attachTestTabAndResume(gClient, "test-logpoint",
function(response, targetFront, threadClient) {
gThreadClient = threadClient;
test_simple_breakpoint();
});
});
do_test_pending();
}
function test_simple_breakpoint() {
gThreadClient.addOneTimeListener("paused", async function(event, packet) {
const source = await getSourceById(
gThreadClient,
packet.frame.where.actor
);
// Set a logpoint which should invoke console.log.
await source.setBreakpoint({
line: 4,
options: { logValue: "a" },
});
// Execute the rest of the code.
gThreadClient.resume();
});
// Sandboxes don't have a console available so we add our own.
/* eslint-disable */
Cu.evalInSandbox("var console = { log: v => { this.logValue = v } };\n" + // 1
"debugger;\n" + // 2
"var a = 'three';\n" + // 3
"var b = 2;\n", // 4
gDebuggee,
"1.8",
"test.js",
1);
/* eslint-enable */
Assert.equal(gDebuggee.logValue, "three");
finishClient(gClient);
}

Просмотреть файл

@ -0,0 +1,62 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Check that conditions are respected when specified in a logpoint.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-logpoint");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect().then(function() {
attachTestTabAndResume(gClient, "test-logpoint",
function(response, targetFront, threadClient) {
gThreadClient = threadClient;
test_simple_breakpoint();
});
});
do_test_pending();
}
function test_simple_breakpoint() {
gThreadClient.addOneTimeListener("paused", async function(event, packet) {
const source = await getSourceById(
gThreadClient,
packet.frame.where.actor
);
// Set a logpoint which should invoke console.log.
await source.setBreakpoint({
line: 5,
options: { logValue: "a", condition: "a === 5" },
});
// Execute the rest of the code.
gThreadClient.resume();
});
// Sandboxes don't have a console available so we add our own.
/* eslint-disable */
Cu.evalInSandbox("var console = { log: v => { this.logValue = v } };\n" + // 1
"debugger;\n" + // 2
"var a = 1;\n" + // 3
"while (a < 10) {\n" + // 4
" a++;\n" + // 5
"}",
gDebuggee,
"1.8",
"test.js",
1);
/* eslint-enable */
Assert.equal(gDebuggee.logValue, 5);
finishClient(gClient);
}

Просмотреть файл

@ -134,6 +134,8 @@ reason = bug 1104838
[test_conditional_breakpoint-01.js]
[test_conditional_breakpoint-02.js]
[test_conditional_breakpoint-03.js]
[test_logpoint-01.js]
[test_logpoint-02.js]
[test_listsources-01.js]
[test_listsources-02.js]
[test_listsources-03.js]

Просмотреть файл

@ -7,7 +7,7 @@
const promise = require("devtools/shared/deprecated-sync-thenables");
const eventSource = require("devtools/shared/client/event-source");
const {DebuggerClient} = require("devtools/shared/client/debugger-client");
const {arg, DebuggerClient} = require("devtools/shared/client/debugger-client");
/**
* Breakpoint clients are used to remove breakpoints that are no longer used.
@ -21,10 +21,10 @@ const {DebuggerClient} = require("devtools/shared/client/debugger-client");
* @param location object
* The location of the breakpoint. This is an object with two properties:
* url and line.
* @param condition string
* The conditional expression of the breakpoint
* @param options object
* Any options associated with the breakpoint
*/
function BreakpointClient(client, sourceClient, actor, location, condition) {
function BreakpointClient(client, sourceClient, actor, location, options) {
this._client = client;
this._actor = actor;
this.location = location;
@ -32,11 +32,7 @@ function BreakpointClient(client, sourceClient, actor, location, condition) {
this.location.url = sourceClient.url;
this.source = sourceClient;
this.request = this._client.request;
// The condition property should only exist if it's a truthy value
if (condition) {
this.condition = condition;
}
this.options = options;
}
BreakpointClient.prototype = {
@ -56,32 +52,46 @@ BreakpointClient.prototype = {
type: "delete",
}),
// Send a setOptions request to newer servers.
setOptionsRequester: DebuggerClient.requester({
type: "setOptions",
options: arg(0),
}, {
before(packet) {
this.options = packet.options;
return packet;
},
}),
/**
* Set the condition of this breakpoint
* Set any options for this breakpoint.
*/
setCondition: function(gThreadClient, condition) {
const deferred = promise.defer();
setOptions: function(options) {
if (this._client.mainRoot.traits.nativeLogpoints) {
this.setOptionsRequester(options);
} else {
// Older servers need to reinstall breakpoints when the condition changes.
const deferred = promise.defer();
const info = {
line: this.location.line,
column: this.location.column,
condition: condition,
};
const info = {
line: this.location.line,
column: this.location.column,
options,
};
// Remove the current breakpoint and add a new one with the
// condition.
this.remove(response => {
if (response && response.error) {
deferred.reject(response);
return;
}
// Remove the current breakpoint and add a new one with the specified
// information.
this.remove(response => {
if (response && response.error) {
deferred.reject(response);
return;
}
deferred.resolve(this.source.setBreakpoint(info).then(([, newBreakpoint]) => {
return newBreakpoint;
}));
});
return deferred.promise;
deferred.resolve(this.source.setBreakpoint(info).then(([, newBreakpoint]) => {
return newBreakpoint;
}));
});
}
},
};

Просмотреть файл

@ -37,6 +37,7 @@ const UnsolicitedNotifications = {
"evaluationResult": "evaluationResult",
"updatedSource": "updatedSource",
"inspectObject": "inspectObject",
"virtualConsoleLog": "virtualConsoleLog",
// newSource is still emitted on the ThreadActor, in addition to the
// BrowsingContextActor we have to keep it here until ThreadClient is converted to

Просмотреть файл

@ -175,10 +175,10 @@ SourceClient.prototype = {
* Request to set a breakpoint in the specified location.
*
* @param object location
* The location and condition of the breakpoint in
* the form of { line[, column, condition] }.
* The location and options of the breakpoint in
* the form of { line[, column, options] }.
*/
setBreakpoint: function({ line, column, condition, noSliding }) {
setBreakpoint: function({ line, column, options, noSliding }) {
// A helper function that sets the breakpoint.
const doSetBreakpoint = callback => {
const location = {
@ -190,10 +190,23 @@ SourceClient.prototype = {
to: this.actor,
type: "setBreakpoint",
location,
condition,
options,
noSliding,
};
// Older servers only support conditions, not a more general options
// object. Transform the packet to support the older format.
if (options && !this._client.mainRoot.traits.nativeLogpoints) {
delete packet.options;
if (options.logValue) {
// Emulate log points by setting a condition with a call to console.log,
// which always returns false so the server will never pause.
packet.condition = `console.log(${options.logValue})`;
} else {
packet.condition = options.condition;
}
}
return this._client.request(packet).then(response => {
// Ignoring errors, since the user may be setting a breakpoint in a
// dead script that will reappear on a page reload.
@ -204,7 +217,7 @@ SourceClient.prototype = {
this,
response.actor,
location,
condition
options
);
}
if (callback) {

Просмотреть файл

@ -3,13 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {generateActorSpec} = require("devtools/shared/protocol");
const {Arg, generateActorSpec} = require("devtools/shared/protocol");
const breakpointSpec = generateActorSpec({
typeName: "breakpoint",
methods: {
delete: {},
setOptions: {
request: {
options: Arg(0, "nullable:json"),
},
},
},
});

Просмотреть файл

@ -61,7 +61,7 @@ const sourceSpec = generateActorSpec({
line: Arg(0, "number"),
column: Arg(1, "nullable:number"),
},
condition: Arg(2, "nullable:string"),
options: Arg(2, "nullable:json"),
noSliding: Arg(3, "nullable:boolean"),
},
response: RetVal("json"),

Просмотреть файл

@ -1,4 +1,4 @@
52987
53000
0/nm
0th/pt
1/n1
@ -15892,8 +15892,10 @@ backslider/M
backspace/DSMG
backspin/M
backsplash/S
backstab/S
backstabber/MS
backstabbing
backstabby
backstage/M
backstair/S
backstop/SM
@ -17311,6 +17313,7 @@ bomber/M
bombproof
bombshell/SM
bombsite/S
bon/S
bona
bonanza/MS
bonbon/MS
@ -20154,6 +20157,8 @@ colloquy/M
collude/DSG
collusion/M
collusive
colocate/DGS
colocation/S
cologne/SM
colon/SM
colonel/SM
@ -21241,7 +21246,7 @@ cosmonaut/SM
cosmopolitan/MS
cosmopolitanism/M
cosmos/MS
cosplay
cosplay/DGSRZ
cosponsor/GSMD
cosset/SGD
cossetted
@ -21846,6 +21851,7 @@ cullender/MS
culminate/XDSGN
culmination/M
culotte/SM
culpa/S
culpability/M
culpable/I
culpably
@ -25747,6 +25753,7 @@ extolled
extolling
extort/SGD
extortion/MRZ
extortionary
extortionate/Y
extortioner/M
extortionist/MS
@ -27980,7 +27987,7 @@ geometry/SM
geophysical
geophysicist/SM
geophysics/M
geopolitical
geopolitical/Y
geopolitics/M
geostationary
geosynchronous
@ -34613,6 +34620,7 @@ mazurka/MS
mañana/M
mdse
me/DSH
mea
mead/M
meadow/MS
meadowlark/MS
@ -39822,6 +39830,7 @@ pivot/MDGS
pivotal
pix/M
pixel/MS
pixelate/DS
pixie/MS
pizazz/M
pizza/MS
@ -43857,7 +43866,7 @@ scalar/S
scalawag/MS
scald/MDSG
scale's
scale/CGDS
scale/CGDSB
scaleless
scalene
scaliness/M
@ -47076,6 +47085,7 @@ stony/TRP
stood
stooge/MS
stool/SM
stoolie/SM
stoop/GSMD
stop's
stop/US
@ -47765,6 +47775,7 @@ supertanker/MS
superuser/S
supervene/GDS
supervention/M
supervillain/SM
supervise/XGNDS
supervised/U
supervision/M
@ -51476,6 +51487,7 @@ vivace
vivacious/PY
vivaciousness/M
vivacity/M
vivant/S
vivaria
vivarium/SM
vivid/RYTP
@ -52389,6 +52401,7 @@ wishbone/SM
wisher/M
wishful/Y
wishlist's
wishy-washy
wisp/MS
wispy/RT
wist

Просмотреть файл

@ -221,13 +221,6 @@ wr::WrSpaceAndClipChain ClipManager::SwitchItem(
// for it.
clips.mClipChainId = DefineClipChain(clip, auPerDevPixel, aStackingContext);
// If we didn't define a clip chain, inherit one from the stack. Eventually
// we should ensure stacking contexts always have a valid clip chain, and
// that should eliminate the need for this.
if (clips.mClipChainId.isNothing() && !mItemClipStack.empty()) {
clips.mClipChainId = mItemClipStack.top().mClipChainId;
}
Maybe<wr::WrSpaceAndClip> spaceAndClip = GetScrollLayer(asr);
MOZ_ASSERT(spaceAndClip.isSome());
clips.mScrollId = SpatialIdAfterOverride(spaceAndClip->space);
@ -363,11 +356,22 @@ Maybe<wr::WrClipChainId> ClipManager::DefineClipChain(
CLIP_LOG("cache[%p] <= %zu\n", chain, clipId.id);
}
if (clipIds.Length() == 0) {
return Nothing();
// Now find the parent display item's clipchain id
Maybe<wr::WrClipChainId> parentChainId;
if (!mItemClipStack.empty()) {
parentChainId = mItemClipStack.top().mClipChainId;
}
return Some(mBuilder->DefineClipChain(clipIds));
// And define the current display item's clipchain using the clips and the
// parent. If the current item has no clips of its own, just use the parent
// item's clipchain.
Maybe<wr::WrClipChainId> chainId;
if (clipIds.Length() > 0) {
chainId = Some(mBuilder->DefineClipChain(parentChainId, clipIds));
} else {
chainId = parentChainId;
}
return chainId;
}
ClipManager::~ClipManager() {

Просмотреть файл

@ -1,13 +0,0 @@
<style>
html, body {
margin: 0;
}
</style>
<div style="filter: drop-shadow(50px 50px 20px red)">
<div style="width:256px; height:256px; border-radius:16px; overflow:hidden">
<div style="width:256px; height:256px; background-color:green;"></div>
</div>
</div>
<!-- add white divs on top to simulate the clip from the test file -->
<div style="background-color: white; position:absolute; left: 0; top: 0; width: 100px; height: 700px"></div>
<div style="background-color: white; position:absolute; left: 0; top: 0; width: 700px; height: 100px"></div>

Просмотреть файл

@ -1,12 +0,0 @@
<style>
html, body {
margin: 0;
}
</style>
<div style="clip: rect(100px,1000px,1000px,100px); position:absolute">
<div style="filter: drop-shadow(50px 50px 20px red)">
<div style="width:256px; height:256px; border-radius:16px; overflow:hidden">
<div style="width:256px; height:256px; background-color:green;"></div>
</div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,4 @@
<!DOCTYPE html>
<html>
<div style="overflow-y:hidden; position:absolute; top:50px; width: 200px; height: 100px; border: solid 1px black">
</div>

Просмотреть файл

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html reftest-async-scroll>
<div style="overflow-y:hidden; position:absolute; top:50px; width: 200px; height: 100px; border: solid 1px black"
reftest-displayport-x="0" reftest-displayport-y="0"
reftest-displayport-w="200" reftest-displayport-h="300"
reftest-async-scroll-x="0" reftest-async-scroll-y="50">
<div style="transform: translateY(0px); width: 100px; height: 300px">
<div style="background-color: green; height: 25px; width: 25px; border-radius: 5px"></div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE html>
<div style="overflow:hidden; width: 300px; height: 200px; border: solid 1px black">
<div style="width: 600px; transform: translateX(0px); will-change: transform">
<svg style="height: 200px" width="600">
<path d="M0 0 H 300 V 25 Z" fill="red"></path>
</svg>
</div>
</div>

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE html>
<div style="overflow:hidden; width: 300px; height: 200px; border: solid 1px black">
<div style="width: 600px; transform: translateX(0px); will-change: transform">
<svg style="height: 200px" width="600">
<path d="M0 0 H 600 V 50 Z" fill="red"></path>
</svg>
</div>
</div>

Просмотреть файл

@ -18,4 +18,5 @@ fuzzy(5-32,21908-26621) fuzzy-if(webrender,0-9,0-100) == 1463802.html 1463802-re
fuzzy(0-11,0-4) == 1474722.html 1474722-ref.html
== 1501195.html 1501195-ref.html
== 1519754.html 1519754-ref.html
fuzzy-if(webrender,6-7,34741-36908) == 1523776.html 1523776-ref.html
skip-if(!asyncPan) == 1524261.html 1524261-ref.html
fuzzy-if(webrender,14-14,44-44) == 1524353.html 1524353-ref.html

Просмотреть файл

@ -701,10 +701,13 @@ void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
}
wr::WrClipChainId DisplayListBuilder::DefineClipChain(
const Maybe<wr::WrClipChainId>& aParent,
const nsTArray<wr::WrClipId>& aClips) {
uint64_t clipchainId = wr_dp_define_clipchain(
mWrState, nullptr, aClips.Elements(), aClips.Length());
WRDL_LOG("DefineClipChain id=%" PRIu64 " clips=%zu\n", mWrState, clipchainId,
uint64_t clipchainId =
wr_dp_define_clipchain(mWrState, aParent ? &(aParent->id) : nullptr,
aClips.Elements(), aClips.Length());
WRDL_LOG("DefineClipChain id=%" PRIu64 " p=%s clips=%zu\n", mWrState,
clipchainId, aParent ? Stringify(aParent->id).c_str() : "(nil)",
aClips.Length());
return wr::WrClipChainId{clipchainId};
}

Просмотреть файл

@ -362,7 +362,8 @@ class DisplayListBuilder {
const wr::RasterSpace& aRasterSpace);
void PopStackingContext(bool aIsReferenceFrame);
wr::WrClipChainId DefineClipChain(const nsTArray<wr::WrClipId>& aClips);
wr::WrClipChainId DefineClipChain(const Maybe<wr::WrClipChainId>& aParent,
const nsTArray<wr::WrClipId>& aClips);
wr::WrClipId DefineClip(
const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,

Просмотреть файл

@ -1541,10 +1541,23 @@ void OutlineTypedObject::setOwnerAndData(JSObject* owner, uint8_t* data) {
owner_ = owner;
data_ = data;
// Trigger a post barrier when attaching an object outside the nursery to
// one that is inside it.
if (owner && !IsInsideNursery(this) && IsInsideNursery(owner)) {
owner->storeBuffer()->putWholeCell(this);
if (owner) {
if (!IsInsideNursery(this) && IsInsideNursery(owner)) {
// Trigger a post barrier when attaching an object outside the nursery to
// one that is inside it.
owner->storeBuffer()->putWholeCell(this);
} else if (IsInsideNursery(this) && !IsInsideNursery(owner)) {
// ...and also when attaching an object inside the nursery to one that is
// outside it, for a subtle reason -- the outline object now points to
// the memory owned by 'owner', and can modify object/string references
// stored in that memory, potentially storing nursery pointers in it. If
// the outline object is in the nursery, then the post barrier will do
// nothing; you will be writing a nursery pointer "into" a nursery
// object. But that will result in the tenured owner's data containing a
// nursery pointer, and thus we need a store buffer edge. Since we can't
// catch the actual write, register the owner preemptively now.
storeBuffer()->putWholeCell(owner);
}
}
}

Просмотреть файл

@ -726,14 +726,23 @@ void UnmapPages(void* region, size_t length) {
}
bool MarkPagesUnused(void* region, size_t length) {
MOZ_RELEASE_ASSERT(region && OffsetFromAligned(region, pageSize) == 0);
MOZ_RELEASE_ASSERT(length > 0 && length % pageSize == 0);
MOZ_RELEASE_ASSERT(region);
MOZ_RELEASE_ASSERT(length > 0);
// pageSize == ArenaSize doesn't necessarily hold, but this function is
// used by the GC to decommit unused Arenas, so we don't want to assert
// if pageSize > ArenaSize.
MOZ_ASSERT(OffsetFromAligned(region, ArenaSize) == 0);
MOZ_ASSERT(length % ArenaSize == 0);
MOZ_MAKE_MEM_NOACCESS(region, length);
if (!DecommitEnabled()) {
return true;
}
// We can't decommit part of a page.
MOZ_RELEASE_ASSERT(OffsetFromAligned(region, pageSize) == 0);
MOZ_RELEASE_ASSERT(length % pageSize == 0);
#if defined(XP_WIN)
return VirtualAlloc(region, length, MEM_RESET,
@ -746,10 +755,23 @@ bool MarkPagesUnused(void* region, size_t length) {
}
void MarkPagesInUse(void* region, size_t length) {
MOZ_RELEASE_ASSERT(region && OffsetFromAligned(region, pageSize) == 0);
MOZ_RELEASE_ASSERT(length > 0 && length % pageSize == 0);
MOZ_RELEASE_ASSERT(region);
MOZ_RELEASE_ASSERT(length > 0);
// pageSize == ArenaSize doesn't necessarily hold, but this function is
// used by the GC to recommit Arenas that were previously decommitted,
// so we don't want to assert if pageSize > ArenaSize.
MOZ_ASSERT(OffsetFromAligned(region, ArenaSize) == 0);
MOZ_ASSERT(length % ArenaSize == 0);
MOZ_MAKE_MEM_UNDEFINED(region, length);
if (!DecommitEnabled()) {
return;
}
// We can't commit part of a page.
MOZ_RELEASE_ASSERT(OffsetFromAligned(region, pageSize) == 0);
MOZ_RELEASE_ASSERT(length % pageSize == 0);
}
size_t GetPageFaultCount() {

Просмотреть файл

@ -7006,11 +7006,8 @@ bool nsDisplayStickyPosition::CreateWebRenderCommands(
}
{
wr::StackingContextParams params;
params.clip =
wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
aBuilder, params);
aBuilder);
nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc,
aManager, aDisplayListBuilder);
}

Просмотреть файл

@ -45,8 +45,8 @@ fails == column-contain-1a.html column-contain-1-ref.html
== column-contain-1b.html column-contain-1-ref.html
== column-contain-2.html column-contain-2-ref.html
== block-in-inline-1.html block-in-inline-1-ref.html
fuzzy-if(skiaContent,0-1,0-22) fuzzy-if((winWidget&&(webrender||!layersGPUAccelerated))||(OSX&&webrender),0-92,0-1369) fuzzy-if(Android,0-8,0-1533) == block-in-inline-2.html block-in-inline-2-ref.html
fuzzy-if(Android,0-8,0-630) fuzzy-if(OSX,0-1,0-11) fuzzy-if(skiaContent,0-1,0-220) fuzzy-if((winWidget&&(webrender||!layersGPUAccelerated))||(OSX&&webrender),0-92,0-1343) == block-in-inline-3.html block-in-inline-3-ref.html
fuzzy-if(skiaContent,0-1,0-22) fuzzy-if(winWidget&&!layersGPUAccelerated,0-116,0-1320) fuzzy-if(Android,0-8,0-1533) == block-in-inline-2.html block-in-inline-2-ref.html
fuzzy-if(Android,0-8,0-630) fuzzy-if(OSX,0-1,0-11) fuzzy-if(skiaContent,0-1,0-220) fuzzy-if(winWidget&&!layersGPUAccelerated,0-116,0-1320) == block-in-inline-3.html block-in-inline-3-ref.html
== block-in-inline-continuations.html block-in-inline-continuations-ref.html
== iframe-1.html iframe-1-ref.html
== transformed-1.html transformed-1-ref.html

Просмотреть файл

@ -1549,8 +1549,6 @@ public class BrowserApp extends GeckoApp
NotificationHelper.destroy();
GeckoNetworkManager.destroy();
EventDispatcher.getInstance().dispatch("Browser:ZombifyTabs", null);
MmaDelegate.flushResources(this);
super.onDestroy();

Просмотреть файл

@ -371,7 +371,6 @@ var BrowserApp = {
GlobalEventDispatcher.registerListener(this, [
"Browser:LoadManifest",
"Browser:Quit",
"Browser:ZombifyTabs",
"Fonts:Reload",
"FormHistory:Init",
"FullScreen:Exit",
@ -1731,13 +1730,6 @@ var BrowserApp = {
this.quit(data);
break;
case "Browser:ZombifyTabs":
let tabs = this._tabs;
for (let i = 0; i < tabs.length; i++) {
tabs[i].zombify();
}
break;
case "Fonts:Reload":
FontEnumerator.updateFontList();
break;

Просмотреть файл

@ -4170,6 +4170,7 @@ int AddPreCompleteActions(ActionList *list) {
rv = action->Parse(line);
if (rv) {
delete action;
free(buf);
return rv;
}

Просмотреть файл

@ -11,26 +11,42 @@
namespace mozilla {
namespace recordreplay {
// In a replaying or middleman process, all middleman calls that have been
// encountered, indexed by their ID.
static StaticInfallibleVector<MiddlemanCall*> gMiddlemanCalls;
// In a replaying or middleman process, association between values produced by
// a middleman call and the call itself.
typedef std::unordered_map<const void*, MiddlemanCall*> MiddlemanCallMap;
static MiddlemanCallMap* gMiddlemanCallMap;
// In a middleman process, any buffers allocated for performed calls.
static StaticInfallibleVector<void*> gAllocatedBuffers;
// State used for keeping track of middleman calls in either a replaying
// process or middleman process.
struct MiddlemanCallState {
// In a replaying or middleman process, all middleman calls that have been
// encountered, indexed by their ID.
InfallibleVector<MiddlemanCall*> mCalls;
// Lock protecting middleman call state.
// In a replaying or middleman process, association between values produced by
// a middleman call and the call itself.
MiddlemanCallMap mCallMap;
// In a middleman process, any buffers allocated for performed calls.
InfallibleVector<void*> mAllocatedBuffers;
};
// In a replaying process, all middleman call state. In a middleman process,
// state for the child currently being processed.
static MiddlemanCallState* gState;
// In a middleman process, middleman call state for each child process, indexed
// by the child ID.
static StaticInfallibleVector<MiddlemanCallState*> gStatePerChild;
// In a replaying process, lock protecting middleman call state. In the
// middleman, all accesses occur on the main thread.
static Monitor* gMonitor;
void InitializeMiddlemanCalls() {
MOZ_RELEASE_ASSERT(IsRecordingOrReplaying() || IsMiddleman());
gMiddlemanCallMap = new MiddlemanCallMap();
gMonitor = new Monitor();
if (IsReplaying()) {
gState = new MiddlemanCallState();
gMonitor = new Monitor();
}
}
// Apply the ReplayInput phase to aCall and any calls it depends on that have
@ -79,9 +95,9 @@ bool SendCallToMiddleman(size_t aCallId, CallArguments* aArguments,
MonitorAutoLock lock(*gMonitor);
// Allocate and fill in a new MiddlemanCall.
size_t id = gMiddlemanCalls.length();
size_t id = gState->mCalls.length();
MiddlemanCall* newCall = new MiddlemanCall();
gMiddlemanCalls.emplaceBack(newCall);
gState->mCalls.emplaceBack(newCall);
newCall->mId = id;
newCall->mCallId = aCallId;
newCall->mArguments.CopyFrom(aArguments);
@ -93,7 +109,7 @@ bool SendCallToMiddleman(size_t aCallId, CallArguments* aArguments,
redirection.mMiddlemanCall(cx);
if (cx.mFailed) {
delete newCall;
gMiddlemanCalls.popBack();
gState->mCalls.popBack();
if (child::CurrentRepaintCannotFail()) {
child::ReportFatalError(Nothing(),
"Middleman call preface failed: %s\n",
@ -154,10 +170,18 @@ bool SendCallToMiddleman(size_t aCallId, CallArguments* aArguments,
return true;
}
void ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
void ProcessMiddlemanCall(size_t aChildId, const char* aInputData, size_t aInputSize,
InfallibleVector<char>* aOutputData) {
MOZ_RELEASE_ASSERT(IsMiddleman());
while (aChildId >= gStatePerChild.length()) {
gStatePerChild.append(nullptr);
}
if (!gStatePerChild[aChildId]) {
gStatePerChild[aChildId] = new MiddlemanCallState();
}
gState = gStatePerChild[aChildId];
BufferStream inputStream(aInputData, aInputSize);
BufferStream outputStream(aOutputData);
@ -192,12 +216,14 @@ void ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
call->mArguments.CopyFrom(&arguments);
call->EncodeOutput(outputStream);
while (call->mId >= gMiddlemanCalls.length()) {
gMiddlemanCalls.emplaceBack(nullptr);
while (call->mId >= gState->mCalls.length()) {
gState->mCalls.emplaceBack(nullptr);
}
MOZ_RELEASE_ASSERT(!gMiddlemanCalls[call->mId]);
gMiddlemanCalls[call->mId] = call;
MOZ_RELEASE_ASSERT(!gState->mCalls[call->mId]);
gState->mCalls[call->mId] = call;
}
gState = nullptr;
}
void* MiddlemanCallContext::AllocateBytes(size_t aSize) {
@ -209,16 +235,25 @@ void* MiddlemanCallContext::AllocateBytes(size_t aSize) {
// of the MiddlemanCall itself) or will be recovered when we rewind after we
// are done with our divergence from the recording (any other phase).
if (IsMiddleman()) {
gAllocatedBuffers.append(rv);
gState->mAllocatedBuffers.append(rv);
}
return rv;
}
void ResetMiddlemanCalls() {
void ResetMiddlemanCalls(size_t aChildId) {
MOZ_RELEASE_ASSERT(IsMiddleman());
for (MiddlemanCall* call : gMiddlemanCalls) {
if (aChildId >= gStatePerChild.length()) {
return;
}
gState = gStatePerChild[aChildId];
if (!gState) {
return;
}
for (MiddlemanCall* call : gState->mCalls) {
if (call) {
CallArguments arguments;
call->mArguments.CopyTo(&arguments);
@ -231,16 +266,18 @@ void ResetMiddlemanCalls() {
// Delete the calls in a second pass. The MiddlemanRelease phase depends on
// previous middleman calls still existing.
for (MiddlemanCall* call : gMiddlemanCalls) {
for (MiddlemanCall* call : gState->mCalls) {
delete call;
}
gMiddlemanCalls.clear();
for (auto buffer : gAllocatedBuffers) {
gState->mCalls.clear();
for (auto buffer : gState->mAllocatedBuffers) {
free(buffer);
}
gAllocatedBuffers.clear();
gMiddlemanCallMap->clear();
gState->mAllocatedBuffers.clear();
gState->mCallMap.clear();
gState = nullptr;
}
///////////////////////////////////////////////////////////////////////////////
@ -248,13 +285,13 @@ void ResetMiddlemanCalls() {
///////////////////////////////////////////////////////////////////////////////
static void AddMiddlemanCallValue(const void* aThing, MiddlemanCall* aCall) {
gMiddlemanCallMap->erase(aThing);
gMiddlemanCallMap->insert(MiddlemanCallMap::value_type(aThing, aCall));
gState->mCallMap.erase(aThing);
gState->mCallMap.insert(MiddlemanCallMap::value_type(aThing, aCall));
}
static MiddlemanCall* LookupMiddlemanCall(const void* aThing) {
MiddlemanCallMap::const_iterator iter = gMiddlemanCallMap->find(aThing);
if (iter != gMiddlemanCallMap->end()) {
MiddlemanCallMap::const_iterator iter = gState->mCallMap.find(aThing);
if (iter != gState->mCallMap.end()) {
return iter->second;
}
return nullptr;
@ -262,9 +299,9 @@ static MiddlemanCall* LookupMiddlemanCall(const void* aThing) {
static const void* GetMiddlemanCallValue(size_t aId) {
MOZ_RELEASE_ASSERT(IsMiddleman());
MOZ_RELEASE_ASSERT(aId < gMiddlemanCalls.length() && gMiddlemanCalls[aId] &&
gMiddlemanCalls[aId]->mMiddlemanValue.isSome());
return gMiddlemanCalls[aId]->mMiddlemanValue.ref();
MOZ_RELEASE_ASSERT(aId < gState->mCalls.length() && gState->mCalls[aId] &&
gState->mCalls[aId]->mMiddlemanValue.isSome());
return gState->mCalls[aId]->mMiddlemanValue.ref();
}
bool MM_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr) {
@ -293,7 +330,7 @@ bool MM_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr) {
case MiddlemanCallPhase::ReplayInput:
if (callId.isSome()) {
aCx.WriteInputScalar(callId.ref());
aCx.mDependentCalls->append(gMiddlemanCalls[callId.ref()]);
aCx.mDependentCalls->append(gState->mCalls[callId.ref()]);
return true;
}
return false;

Просмотреть файл

@ -343,12 +343,14 @@ bool SendCallToMiddleman(size_t aCallId, CallArguments* aArguments,
bool aDiverged);
// In the middleman process, perform one or more calls encoded in aInputData
// and encode their outputs to aOutputData.
void ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
// and encode their outputs to aOutputData. The calls are associated with the
// specified child process ID.
void ProcessMiddlemanCall(size_t aChildId,
const char* aInputData, size_t aInputSize,
InfallibleVector<char>* aOutputData);
// In the middleman process, reset all call state.
void ResetMiddlemanCalls();
// In the middleman process, reset all call state for a child process ID.
void ResetMiddlemanCalls(size_t aChildId);
///////////////////////////////////////////////////////////////////////////////
// Middleman Call Helpers

Просмотреть файл

@ -434,14 +434,6 @@ typedef BinaryMessage<MessageType::MiddlemanCallResponse>
typedef EmptyMessage<MessageType::ResetMiddlemanCalls>
ResetMiddlemanCallsMessage;
static inline MiddlemanCallResponseMessage* ProcessMiddlemanCallMessage(
const MiddlemanCallRequestMessage& aMsg) {
InfallibleVector<char> outputData;
ProcessMiddlemanCall(aMsg.BinaryData(), aMsg.BinaryDataSize(), &outputData);
return MiddlemanCallResponseMessage::New(outputData.begin(),
outputData.length());
}
class Channel {
public:
// Note: the handler is responsible for freeing its input message. It will be

Просмотреть файл

@ -94,12 +94,17 @@ void ChildProcessInfo::OnIncomingMessage(const Message& aMsg,
case MessageType::MiddlemanCallRequest: {
const MiddlemanCallRequestMessage& nmsg =
static_cast<const MiddlemanCallRequestMessage&>(aMsg);
Message::UniquePtr response(ProcessMiddlemanCallMessage(nmsg));
InfallibleVector<char> outputData;
ProcessMiddlemanCall(GetId(), nmsg.BinaryData(), nmsg.BinaryDataSize(),
&outputData);
Message::UniquePtr response(
MiddlemanCallResponseMessage::New(outputData.begin(),
outputData.length()));
SendMessage(*response);
break;
}
case MessageType::ResetMiddlemanCalls:
ResetMiddlemanCalls();
ResetMiddlemanCalls(GetId());
break;
default:
break;

Просмотреть файл

@ -636,13 +636,8 @@ static void ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir,
// execv on Windows.
if (restart) {
exit(execv(updaterPath.get(), argv));
}
*outpid = fork();
if (*outpid == -1) {
delete[] argv;
return;
} else if (*outpid == 0) {
exit(execv(updaterPath.get(), argv));
} else {
*outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
}
delete[] argv;
#elif defined(XP_WIN)
@ -707,25 +702,6 @@ static bool ProcessHasTerminated(ProcessType pt) {
#elif defined(XP_MACOSX)
// We're waiting for the process to terminate in LaunchChildMac.
return true;
#elif defined(XP_UNIX)
int exitStatus;
pid_t exited = waitpid(pt, &exitStatus, WNOHANG);
if (exited == 0) {
// Process is still running.
sleep(1);
return false;
}
if (exited == -1) {
LOG(("Error while checking if the updater process is finished"));
// This shouldn't happen, but if it does, the updater process is lost to us,
// so the best we can do is pretend that it's exited.
return true;
}
// If we get here, the process has exited; make sure it exited normally.
if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
LOG(("Error while running the updater process, check update.log"));
}
return true;
#else
// No way to have a non-blocking implementation on these platforms,
// because we're using NSPR and it only provides a blocking wait.

Просмотреть файл

@ -19,7 +19,7 @@ class nsIFile;
#if defined(XP_WIN)
# include <windows.h>
typedef HANDLE ProcessType;
#elif defined(XP_UNIX)
#elif defined(XP_MACOSX)
typedef pid_t ProcessType;
#else
# include "prproces.h"

Просмотреть файл

@ -866,7 +866,8 @@ nsThread::Shutdown() {
}
nsThreadShutdownContext* maybeContext = ShutdownInternal(/* aSync = */ true);
NS_ENSURE_TRUE(maybeContext, NS_ERROR_UNEXPECTED);
if (!maybeContext) return NS_ERROR_UNEXPECTED;
NotNull<nsThreadShutdownContext*> context = WrapNotNull(maybeContext);
// Process events on the current thread until we receive a shutdown ACK.