зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
d6dd350c66
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче