mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 857765: Log errors thrown by user-defined PeerConnection callbacks r=msucan
This commit is contained in:
parent
31c06a0c04
commit
035532f753
@ -727,14 +727,12 @@ PeerConnection.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// This is a separate object because we don't want to expose it to DOM.
|
||||
function PeerConnectionObserver(dompc) {
|
||||
this._dompc = dompc;
|
||||
function RTCError(code, message) {
|
||||
this.name = this.reasonName[Math.min(code, this.reasonName.length - 1)];
|
||||
this.message = (typeof message === "string")? message : this.name;
|
||||
this.__exposedProps__ = { name: "rw", message: "rw" };
|
||||
}
|
||||
PeerConnectionObserver.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
RTCError.prototype = {
|
||||
// These strings must match those defined in the WebRTC spec.
|
||||
reasonName: [
|
||||
"NO_ERROR", // Should never happen -- only used for testing
|
||||
@ -747,69 +745,106 @@ PeerConnectionObserver.prototype = {
|
||||
"INCOMPATIBLE_CONSTRAINTS",
|
||||
"INCOMPATIBLE_MEDIASTREAMTRACK",
|
||||
"INTERNAL_ERROR"
|
||||
],
|
||||
]
|
||||
};
|
||||
|
||||
callErrorCallback: function(callback, code, message) {
|
||||
if (code > Ci.IPeerConnection.kMaxErrorType) {
|
||||
code = Ci.IPeerConnection.kInternalError;
|
||||
}
|
||||
|
||||
if (typeof message !== "string") {
|
||||
message = this.reasonName[code];
|
||||
// This is a separate object because we don't want to expose it to DOM.
|
||||
function PeerConnectionObserver(dompc) {
|
||||
this._dompc = dompc;
|
||||
}
|
||||
PeerConnectionObserver.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
callCB: function(callback, arg) {
|
||||
if (callback) {
|
||||
try {
|
||||
callback.onCallback({
|
||||
name: this.reasonName[code], message: message,
|
||||
__exposedProps__: { name: "rw", message: "rw" }
|
||||
});
|
||||
} catch(e) {}
|
||||
callback.onCallback(arg);
|
||||
} catch(e) {
|
||||
// A content script (user-provided) callback threw an error. We don't
|
||||
// want this to take down peerconnection, but we still want the user
|
||||
// to see it, so we catch it, report it, and move on.
|
||||
//
|
||||
// We do stack parsing in two different places for different reasons:
|
||||
|
||||
var msg;
|
||||
if (e.result == Cr.NS_ERROR_XPC_JS_THREW_JS_OBJECT) {
|
||||
// TODO(jib@mozilla.com): Revisit once bug 862153 is fixed.
|
||||
//
|
||||
// The actual content script frame is unavailable due to bug 862153,
|
||||
// so users see file and line # into this file, which is not helpful.
|
||||
//
|
||||
// 1) Fix up the error message itself to differentiate between the
|
||||
// 22 places we call callCB() in this file, using plain JS stack.
|
||||
//
|
||||
// Tweak the existing NS_ERROR_XPC_JS_THREW_JS_OBJECT message:
|
||||
// -'Error: x' when calling method: [RTCPeerConCallback::onCallback]
|
||||
// +'Error: x' when calling method: [RTCPeerConCallback::onCreateOfferError]
|
||||
|
||||
let caller = Error().stack.split("\n")[1].split("@")[0];
|
||||
// caller ~= "PeerConnectionObserver.prototype.onCreateOfferError"
|
||||
|
||||
msg = e.message.replace("::onCallback", "::" + caller.split(".")[2]);
|
||||
} else {
|
||||
msg = e.message;
|
||||
}
|
||||
|
||||
// Log error message to web console and window.onerror, if present.
|
||||
//
|
||||
// 2) nsIScriptError doesn't understand the nsIStackFrame format, so
|
||||
// do the translation by extracting file and line from XPCOM stack:
|
||||
//
|
||||
// e.location ~= "JS frame :: file://.js :: RTCPCCb::onCallback :: line 1"
|
||||
|
||||
let stack = e.location.toString().split(" :: ");
|
||||
let file = stack[1];
|
||||
let line = parseInt(stack[3].split(" ")[1]);
|
||||
|
||||
let scriptError = Cc["@mozilla.org/scripterror;1"].
|
||||
createInstance(Ci.nsIScriptError);
|
||||
scriptError.initWithWindowID(msg, file, null, line, 0,
|
||||
Ci.nsIScriptError.exceptionFlag,
|
||||
"content javascript",
|
||||
this._dompc._winID);
|
||||
Cc["@mozilla.org/consoleservice;1"].
|
||||
getService(Ci.nsIConsoleService).logMessage(scriptError);
|
||||
|
||||
// Call onerror directly if present (necessary for testing)
|
||||
if (typeof this._dompc._win.onerror === "function") {
|
||||
this._dompc._win.onerror(msg, file, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onCreateOfferSuccess: function(offer) {
|
||||
if (this._dompc._onCreateOfferSuccess) {
|
||||
try {
|
||||
this._dompc._onCreateOfferSuccess.onCallback({
|
||||
type: "offer", sdp: offer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc._onCreateOfferSuccess,
|
||||
{ type: "offer", sdp: offer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" } });
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onCreateOfferError: function(code, message) {
|
||||
this.callErrorCallback (this._dompc._onCreateOfferFailure, code, message);
|
||||
this.callCB(this._dompc._onCreateOfferFailure, new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onCreateAnswerSuccess: function(answer) {
|
||||
if (this._dompc._onCreateAnswerSuccess) {
|
||||
try {
|
||||
this._dompc._onCreateAnswerSuccess.onCallback({
|
||||
type: "answer", sdp: answer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB (this._dompc._onCreateAnswerSuccess,
|
||||
{ type: "answer", sdp: answer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" } });
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onCreateAnswerError: function(code, message) {
|
||||
this.callErrorCallback (this._dompc._onCreateAnswerFailure, code, message);
|
||||
this.callCB(this._dompc._onCreateAnswerFailure, new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onSetLocalDescriptionSuccess: function() {
|
||||
this._dompc._localType = this._dompc._pendingType;
|
||||
this._dompc._pendingType = null;
|
||||
if (this._dompc._onSetLocalDescriptionSuccess) {
|
||||
try {
|
||||
this._dompc._onSetLocalDescriptionSuccess.onCallback();
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc._onSetLocalDescriptionSuccess);
|
||||
|
||||
// Until we support generating trickle ICE candidates,
|
||||
// we go ahead and trigger a call of onicecandidate here.
|
||||
@ -825,39 +860,33 @@ PeerConnectionObserver.prototype = {
|
||||
onSetRemoteDescriptionSuccess: function() {
|
||||
this._dompc._remoteType = this._dompc._pendingType;
|
||||
this._dompc._pendingType = null;
|
||||
if (this._dompc._onSetRemoteDescriptionSuccess) {
|
||||
try {
|
||||
this._dompc._onSetRemoteDescriptionSuccess.onCallback();
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc._onSetRemoteDescriptionSuccess);
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onSetLocalDescriptionError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callErrorCallback (this._dompc._onSetLocalDescriptionFailure, code,
|
||||
message);
|
||||
this.callCB(this._dompc._onSetLocalDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onSetRemoteDescriptionError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callErrorCallback (this._dompc._onSetRemoteDescriptionFailure, code,
|
||||
message);
|
||||
this.callCB(this._dompc._onSetRemoteDescriptionFailure,
|
||||
new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onAddIceCandidateSuccess: function() {
|
||||
this._dompc._pendingType = null;
|
||||
if (this._dompc._onAddIceCandidateSuccess) {
|
||||
this._dompc._onAddIceCandidateSuccess.onCallback();
|
||||
}
|
||||
this.callCB(this._dompc._onAddIceCandidateSuccess);
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onAddIceCandidateError: function(code, message) {
|
||||
this._dompc._pendingType = null;
|
||||
this.callErrorCallback (this._dompc._onAddIceCandidateError, code, message);
|
||||
this.callCB(this._dompc._onAddIceCandidateError, new RTCError(code, message));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
@ -866,42 +895,24 @@ PeerConnectionObserver.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let iceCb = function() {};
|
||||
let iceGatherCb = function() {};
|
||||
if (this._dompc.onicechange) {
|
||||
iceCb = function(args) {
|
||||
try {
|
||||
self._dompc.onicechange(args);
|
||||
} catch(e) {}
|
||||
};
|
||||
}
|
||||
if (this._dompc.ongatheringchange) {
|
||||
iceGatherCb = function(args) {
|
||||
try {
|
||||
self._dompc.ongatheringchange(args);
|
||||
} catch(e) {}
|
||||
};
|
||||
}
|
||||
|
||||
switch (this._dompc._pc.iceState) {
|
||||
case Ci.IPeerConnection.kIceGathering:
|
||||
iceGatherCb("gathering");
|
||||
this.callCB(this._dompc.ongatheringchange, "gathering");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceWaiting:
|
||||
iceCb("starting");
|
||||
this.callCB(this._dompc.onicechange, "starting");
|
||||
this._dompc._executeNext();
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceChecking:
|
||||
iceCb("checking");
|
||||
this.callCB(this._dompc.onicechange, "checking");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceConnected:
|
||||
// ICE gathering complete.
|
||||
iceCb("connected");
|
||||
iceGatherCb("complete");
|
||||
this.callCB(this._dompc.onicechange, "connected");
|
||||
this.callCB(this._dompc.ongatheringchange, "complete");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceFailed:
|
||||
iceCb("failed");
|
||||
this.callCB(this._dompc.onicechange, "failed");
|
||||
break;
|
||||
default:
|
||||
// Unknown state!
|
||||
@ -910,63 +921,33 @@ PeerConnectionObserver.prototype = {
|
||||
},
|
||||
|
||||
onAddStream: function(stream, type) {
|
||||
if (this._dompc.onaddstream) {
|
||||
try {
|
||||
this._dompc.onaddstream.onCallback({
|
||||
stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc.onaddstream,
|
||||
{ stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" } });
|
||||
},
|
||||
|
||||
onRemoveStream: function(stream, type) {
|
||||
if (this._dompc.onremovestream) {
|
||||
try {
|
||||
this._dompc.onremovestream.onCallback({
|
||||
stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc.onremovestream,
|
||||
{ stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" } });
|
||||
},
|
||||
|
||||
foundIceCandidate: function(cand) {
|
||||
if (this._dompc.onicecandidate) {
|
||||
try {
|
||||
this._dompc.onicecandidate.onCallback({
|
||||
candidate: cand,
|
||||
__exposedProps__: { candidate: "rw" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc.onicecandidate,
|
||||
{candidate: cand, __exposedProps__: { candidate: "rw" } });
|
||||
},
|
||||
|
||||
notifyDataChannel: function(channel) {
|
||||
if (this._dompc.ondatachannel) {
|
||||
try {
|
||||
this._dompc.ondatachannel.onCallback({
|
||||
channel: channel,
|
||||
__exposedProps__: { channel: "r" }
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB(this._dompc.ondatachannel,
|
||||
{ channel: channel, __exposedProps__: { channel: "r" } });
|
||||
},
|
||||
|
||||
notifyConnection: function() {
|
||||
if (this._dompc.onconnection) {
|
||||
try {
|
||||
this._dompc.onconnection.onCallback();
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB (this._dompc.onconnection);
|
||||
},
|
||||
|
||||
notifyClosedConnection: function() {
|
||||
if (this._dompc.onclosedconnection) {
|
||||
try {
|
||||
this._dompc.onclosedconnection.onCallback();
|
||||
} catch(e) {}
|
||||
}
|
||||
this.callCB (this._dompc.onclosedconnection);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,6 +33,7 @@ MOCHITEST_FILES = \
|
||||
test_peerConnection_offerRequiresReceiveAudio.html \
|
||||
test_peerConnection_offerRequiresReceiveVideo.html \
|
||||
test_peerConnection_offerRequiresReceiveVideoAudio.html \
|
||||
test_peerConnection_throwInCallbacks.html \
|
||||
test_peerConnection_bug822674.html \
|
||||
test_peerConnection_bug825703.html \
|
||||
test_peerConnection_bug827843.html \
|
||||
|
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="head.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
createHTML({
|
||||
bug: "857765",
|
||||
title: "Throw in PeerConnection callbacks"
|
||||
});
|
||||
|
||||
let error_count = 0;
|
||||
let oldOnError = window.onerror;
|
||||
window.onerror = function (errorMsg, url, lineNumber) {
|
||||
if (errorMsg.indexOf("Expected") == -1) {
|
||||
getFail(new Error)(errorMsg);
|
||||
}
|
||||
error_count += 1;
|
||||
info("onerror " + error_count + ": " + errorMsg);
|
||||
if (error_count == 7) {
|
||||
finish();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let pc0, pc1, pc2;
|
||||
|
||||
runTest(function () {
|
||||
error_count = 0;
|
||||
|
||||
// Test failure callbacks (limited to 1 for now)
|
||||
pc0 = new mozRTCPeerConnection();
|
||||
pc0.createOffer(getFail(new Error), function(err) {
|
||||
pc1 = new mozRTCPeerConnection();
|
||||
pc2 = new mozRTCPeerConnection();
|
||||
|
||||
// Test success callbacks (happy path)
|
||||
navigator.mozGetUserMedia({video:true, fake: true}, function(video1) {
|
||||
pc1.addStream(video1);
|
||||
pc1.createOffer(function(offer) {
|
||||
pc1.setLocalDescription(offer, function() {
|
||||
pc2.setRemoteDescription(offer, function() {
|
||||
pc2.createAnswer(function(answer) {
|
||||
pc2.setLocalDescription(answer, function() {
|
||||
pc1.setRemoteDescription(answer, function() {
|
||||
throw new Error("Expected");
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
});
|
||||
}, getFail(new Error));
|
||||
throw new Error("Expected");
|
||||
});
|
||||
}, 1);
|
||||
|
||||
function finish() {
|
||||
window.onerror = oldOnError;
|
||||
ok(error_count == 7, "Seven expected errors verified.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getFail(where) {
|
||||
return function (err) {
|
||||
window.onerror = onOldError;
|
||||
unexpectedCallbackAndFinish(where)(err);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user