This question already has answers here:
Unchecked runtime.lastError when using Chrome API
(5 answers)
Closed 5 months ago.
In my extension I'm trying to attach debugger for given tabId, Here is my code
if(!attachedTabIds.includes(tabId)) {
chrome.debugger.attach({ tabId: tabId }, version, function(){
...
})
}
So from time to time I'm getting this error
I tried to wrap it with try/catch block but it didn't work.
try {
chrome.debugger.attach({ tabId: tabId }, version, function(){
...
})
} catch(e){
console.log('e', e)
}
SO how can I handle this error ?
You handle the error by checking the value of chrome.runtime.lastError in the callback function that you passed to chrome.debugger.attach()
if (!attachedTabIds.includes(tabId)) {
chrome.debugger.attach(
{tabId: tabId},
version,
function() {
if (chrome.runtime.lastError) {
console.log("Error");
console.log(chrome.runtime.lastError);
}
else {
console.log("Success");
}
}
);
}
https://developer.chrome.com/docs/extensions/reference/runtime/#property-lastError
lastError
This will be defined during an API method callback if there was an error
Related
I have this helper method on Angular6 class:
saveToLocalStorage(key: string, val: any): Promise<void | LastErrorType> {
return new Promise(function(resolve, reject) {
chrome.storage.local.set({ [key]: val }, function() {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
});
}
Looking at this code again after a few months it has me wondering, should I manually set the error to null here:
chrome.storage.local.set({ [key]: val }, function() {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
chrome.runtime.lastError = null; // <<< necessary?
} else {
resolve();
}
});
Or does chrome.runtime handle this somehow? Obvious, later in the program if chrome.runtime.lastError is still defined, I would be seeing an error that may no longer be relevant? Anyone know what I am talking about?
No, you don't have to do it.
According to the docs, chrome.runtime.lastError will only be set for the callback chain of the specific event that raised the error.
Once you're handling another event, it will be reset.
I am saving some settings using the following sequence
var getSettings = async function() {
var settings;
try {
settings = await authenticatedGET(server_url + SETTINGS_ENDPOINT);
return settings;
} catch (error) {
console.log("Settings Fetch Failed: " + error);
throw new Error(error);
}
}
const setLocalSettings = function(settings) {
chrome.storage.sync.set({ 'LML_Settings': JSON.parse(settings) }, function() {
console.log("Settings saved locally");
});
}
At the line right after the setLocalSettings function definition, the 'settings' object logs out as
{"email_format":"individual","close_tab":true,"frequency":"DAILY"} (correctly as intended). When I go to fetch the settings using this sequence:
chrome.storage.sync.get('LML_Settings', function(LMLSettingsContainer) {
console.log(LMLSettingsContainer);
if (LMLSettingsContainer.LML_settings.close_tab == "true") {
closeCurrentTab();
}
})
LMLSettingsContainer logs out as
{
"LML_Settings": {
"close_tab": true,
"email_format": "individual",
"frequency": "DAILY"
}
}
accessing my settings with LMLSettingsContainer.LML_Settings["<setting>"] is a bit annoying (and its the whole reason I named the top variable LMLSettingsContainer).
Does anyone know if there's a way to have chrome save/get these values unwrapped?
chrome.storage.sync.get('LML_Settings', ({LML_settings}) => { ... }) works, per #wOxxOm
I'm trying to implement a very simple video chat based on the WebRTC API.
Unfortunately my Code is just working from Chrome-to-Chrome and from Firefox-to-Firefox so far.
If I try it from Chrome-to-Firefox or from Firefox-to-Chrome I get the following error output:
Failed to set local offer sdp: Session error code: ERROR_CONTENT. Session error description: Failed to set local video description recv parameters..(anonymous function) # helloWebRtc.js:126***
Did I possibly missed something or do I need some flags in the Chrome or Firefox browser?
Do you have any idea? I would be grateful for any help I can get to solve this issue.
Thank you all in advance!
My helloWebRtc.js looks like this:
var localVideo = document.querySelector("#localVideo");
var remoteVideo = document.querySelector("#remoteVideo");
var SIGNAL_ROOM = "signal_room";
var CHAT_ROOM = "chat_room";
var serverConfig = {
"iceServers": [
{
"urls": "stun:stun.l.google.com:19302"
}
]
};
var optionalConfig = {
optional: [
{
RtpDataChannels: true
},
{
DtlsSrtpKeyAgreement: true
}
]
};
var rtcPeerConn,
localStream;
io = io.connect();
io.emit("ready", {"chat_room": CHAT_ROOM, "signal_room": SIGNAL_ROOM});
io.emit("signal", {
"room": SIGNAL_ROOM,
"type": "user_here",
"message": "new user joined the room"
});
io.on("rtcSignaling", function(data) {
if(!rtcPeerConn) {
startSignaling();
}
if(data.type !== "user_here" && data.message) {
var message = JSON.parse(data.message);
if(message.description) {
var remoteDesc = new RTCSessionDescription(message.description);
rtcPeerConn.setRemoteDescription(remoteDesc, function() {
// if we receive an offer we need to answer
if(rtcPeerConn.remoteDescription.type === "offer") {
rtcPeerConn.createAnswer(sendLocalDescription, function(error) {
console.error("error on creating answer", error);
});
}
}, function(error) {
console.error("error on set remote description", error);
});
} else if(message.candidate) {
var iceCandidate = new RTCIceCandidate(message.candidate);
rtcPeerConn.addIceCandidate(iceCandidate);
}
}
});
function startSignaling() {
rtcPeerConn = new RTCPeerConnection(serverConfig, optionalConfig);
//send any ice candidate to the other peer
rtcPeerConn.onicecandidate = function(event) {
if(event.candidate) {
io.emit("signal", {
"room": SIGNAL_ROOM,
"type": "candidate",
"message": JSON.stringify({
"candidate": event.candidate
})
});
}
};
rtcPeerConn.onnegotiationneeded = function() {
rtcPeerConn.createOffer(sendLocalDescription, function(error) {
console.error("error on creating offer", error);
});
};
// add the other peer's stream
rtcPeerConn.onaddstream = function(event) {
console.info("on add stream called");
remoteVideo.srcObject = event.stream;
};
// add local stream
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(function(stream) {
localVideo.srcObject = stream;
localStream = stream;
rtcPeerConn.addStream(localStream);
})
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
function sendLocalDescription(description) {
rtcPeerConn.setLocalDescription(
description,
function() {
io.emit("signal", {
"room": SIGNAL_ROOM,
"type": "description",
"message": JSON.stringify({
"description": rtcPeerConn.localDescription
})
});
},
function(error) {
console.error("error to set local desc", error);
}
);
}
My NodeJS server (using express.io) looks like the following:
var express = require('express.io');
var app = express();
var PORT = 8686;
app.http().io();
console.log('server started # localhost:8686');
// declaring folders to access i.e.g html files
app.use(express.static(__dirname + '/views'));
app.use('/scripts', express.static(__dirname + '/scripts'));
// root url i.e. "localhost:8686/"
app.get('/', function(req, res) {
res.sendFile('index.html');
});
/**
* Socket.IO Routes for signaling pruposes
*/
app.io.route('ready', function(req) {
req.io.join(req.data.chat_room);
req.io.join(req.data.signal_room);
app.io.room(req.data.chat_room).broadcast('announce', {
message: 'New client in the ' + req.data.chat_room + ' room.'
});
});
app.io.route('send', function(req) {
app.io.room(req.data.room).broadcast('message', {
message: req.data.message,
author: req.data.author
});
});
app.io.route('signal', function(req) {
// Note: req means just broadcasting without letting the sender also receive their own message
if(req.data.type === "description" || req.data.type === "candidate")
req.io.room(req.data.room).broadcast('rtcSignaling', {
type: req.data.type,
message: req.data.message
});
else
req.io.room(req.data.room).broadcast('rtcSignaling', {
type: req.data.type
});
});
app.listen(PORT);
You can compare the offer SDP generated by the chrome and firefox, there might be some difference which is not interoperable to other.
Edit to the old answer below: there are several bugs in interoperability between Chrome and Firefox. Someone from the webrtc team gave me the suggestion to keep the offerer to the same party. So if A creates an offer when setting up a stream to B, then B asks A to create a new offer, instead of creating one self, when setting up a stream to A.
See also here:
https://bugs.chromium.org/p/webrtc/issues/detail?id=5499#c15
I did note that if Firefox initiates a session, Chrome will kick the stream coming from Firefox out of the video element but you can create a new object URL on the stream and set it as the source.
Hope that helps.
Old message:
I am experiencing the same thing, so if you have an answer, I'm curious.
I do believe that there is a mismatch (bug) between FireFox and Chrome in setting up DTLS roles, see also:
https://bugs.chromium.org/p/webrtc/issues/detail?id=2782#c26
just check if you are setting DtlsSrtpKeyAgreement parameter to true while you create the peerconnection.
pc = new RTCPeerConnection(pc_config,{optional: [{RtpDataChannels: true},{
DtlsSrtpKeyAgreement: true}]});
I have been trying to get WebRTC to function with a broadcaster and multiple listeners but am stuck when it comes to transferal descriptions and candidates via signalling (with nodejs & socket.io).
I can get the process working between two browsers with a simple nodejs socket app which simply broadcasts the descriptions and candidates to other already connected clients, but when I attempt to store a description and connect with a newly opened browser, nothing happens.
What I basically need to understand is what do I need to provide to one browser, in order for it to begin communicating with another? The project I am working on requires the ability for listeners to join rooms, authenticate, and begin listening to whatever media is being sent.
Below is my client side code:
var audioContext = new webkitAudioContext()
var client = null
var configuration =
{
'iceServers':
[{
'url': 'stun:stun.example.org'
}]
}
$(function ()
{
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection
client = new RTCPeerConnection(configuration, { optional:[ { RtpDataChannels: true } ]})
client.onnegotiationneeded = function ()
{
console.log('Negotiation needed')
createOffer()
}
client.onicecandidate = function (event)
{
console.log('onicecandidate')
socket.emit('candidate', JSON.stringify({ 'candidate': event.candidate }))
}
client.onaddstream = function (event)
{
console.log('onaddstream')
$('#player').attr('src', URL.createObjectURL(event.stream))
player.play()
}
socket.on('candidate', function (event)
{
candidate(event)
})
socket.on('description', function (message)
{
if(!client) { return }
client.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
if (client.remoteDescription.type == 'offer')
client.createAnswer(function (description)
{
client.setLocalDescription(description, function ()
{
socket.emit('description', JSON.stringify({ 'sdp':client.localDescription }))
})
}, function (err)
{
console.log('error: ' + err)
})
}, function(err)
{
console.log('error: ' + err)
})
})
addStream()
})
function createOffer ()
{
if(!client) { return; }
client.createOffer(function (description)
{
console.log(description)
client.setLocalDescription(description, function ()
{
socket.emit('description', JSON.stringify({ 'sdp': client.localDescription }))
console.log('set local description')
})
})
}
function candidate (message)
{
if(message.candidate)
{
console.log('candidate')
client.addIceCandidate(new RTCIceCandidate(message.candidate))
}
}
function addStream ()
{
navigator.webkitGetUserMedia({audio: true, video: false}, function(stream)
{
client.addStream(stream)
})
}
And my signalling part of my server as it currently stands:
io.on 'connection', (socket) ->
socket.on 'description', (data) ->
parsed = JSON.parse data
socket.broadcast.emit 'description', parsed
socket.on 'candidate', (candidate) ->
parsed = JSON.parse candidate
socket.broadcast.emit 'candidate', parsed
I'd appreciate any insight into this. Thanks.
The "PeerConnection" as the name indicates can be used with only one other peer. You cannot cache the offer SDP generated by one PeerConnection instance to use it with more than one other peers.
In your case, you must create a PeerConnection for each browser that you want to send/receive audio and video from and then exchange the corresponding SDP offer and answers with those browsers via your signaling mechanism.
Please feel free to go through some of the links I have mentioned here to understand how WebRTC works.
Updated code and issue:
I am creating a test harness for my RPC server. Currently it consists of a page which immeadiately fires off an AJAX request to retrieve all functions on the server. Once that is returned it creates a list of buttons so I can click to test. Eventually I will add dialog boxes to test parameter passing to the functions but currently I want to just fire off the basic request when I click the button. The issue I am seeing is that the onclick function is always firing the last function in the list presumably because when the click is fired key is set to the last value in the array. I thought to pass button.innerHTML value but that too suffers that the last button.innerHTML is that of the final key.
What do I need to do to fire off the action correctly?
Here is the business end of the code:
$(document).ready(function() {
$.jsonRPC.setup({
endPoint: '//api.localhost/index.php'
});
$.jsonRPC.request('getExampleData', {
params: [],
success: function(result) {
for (var key in result.result) {
console.log(key+' => '+result.result[key]);
var button = document.createElement('button');
button.innerHTML = result.result[key];
button.onclick = function() { callRPCFunction(result.result[key]); return false; }
var foo = document.getElementById("page");
foo.appendChild(button);
}
},
error: function(result) {
console.log(result);
}
});
});
function callRPCFunction(target) {
$.jsonRPC.request(target, {
params: [],
success: function(result) {
console.log(result);
},
error: function(result) {
console.log(result);
}
});
}
Assignment to element.onClick will not work until the element is added to the DOM. You may call element.onClick(callRPCFunction(result.result[key])); after foo.appendChild(element);. That might work!
You may use jQuery's live() here, it was created for these purposes:
$(element).live('click', callRPCFunction(result.result[key])