how to save screen share stream in webRTC app - javascript

I have a video application written in asp.net and c# with webRTC in JS.
some outsource wrote it for me a while ago (can't reach him any more) the application is working really good, except for one problem.
this video application was written for screen sharing use, and it uses some chrome extension for the screen sharing that also work good. the problem is that as soon as you go back from the screen sharing to the webcam stream, you cannot go back to the screen sharing stream. and it look like the programmer wrote this like this.
i tried turning on the extension again but it sends me an error. my question is, is there a way i can save the screen sharing stream and change the video feed with out touching the extension?
this is the js code for webrtc:
main problem in getfeed in the third 'if'
getFeed = function (shareScreen, username, userType, reinit, cb) {
if (shareScreen) {
$("#screen-share").attr("disabled", "disabled");
$("#video-share").removeAttr("disabled");
// getUserMedia(session, onSuccess, onError);
if (webrtcDetectedBrowser == "chrome") {
DetectRTC.screen.isChromeExtensionAvailable(function (isAvaliabe) {
if (isInScreenSharing) {
return;
}
if (isAvaliabe) {
isInScreenSharing = true;
captureUserMedia(onSuccess);
} else {
alertify.confirm("Screen sharing extension is not installed," +
" do you wish to install it now?",
function(e) {
if (e) {
window.open("https://chrome.google.com/webstore/detail/screen-capturing-
for-micr/hmppjaalobpkbpneompfdpfilcmfhfik", "_blank");
alertify.alert("Click Ok to reload page
after installing extension ", function () {
location.href = location.href;
});
}
});
}
});
} else {
isInScreenSharing = false;
getScreenId(function (error, sourceId, screen_constraints) {
screen_constraints.video = _showWebCam;
screen_constraints.audio = _showWebCam;
getUserMedia(screen_constraints, onSuccess, onError);
});
}
} else {
$("#video-share").attr("disabled", "disabled");
$("#screen-share").removeAttr("disabled");
getUserMedia(
{
// Permissions to request
video: _showWebCam,
audio: _showWebCam
}, onSuccess, onError);
}
function onSuccess(stream) { // succcess callback gives us a media stream
$('.instructions').hide();
if (reinit) {
if (screenStream) {
screenStream.getTracks().forEach(function (e) {
e.stop();
});
screenStream = null;
}
// Store off the stream reference so we can share it later
_mediaStream = stream;
if (webrtcDetectedBrowser == "chrome") {
if (shareScreen) {
//get the audio track from video stream and put it in screeen stream
var audioTrack = videoStream.getAudioTracks()[0];
var screenAudio = stream.getAudioTracks()[0];
if (screenAudio) {
_mediaStream.removeTrack(screenAudio)
}
_mediaStream.addTrack(audioTrack)
screenStream = _mediaStream;
} else {
videoStream = stream;
}
}
// Load the stream into a video element so it starts playing in the UI
console.log('playing my local video feed');
var videoElement = document.querySelector('.video.mine');
attachMediaStream(videoElement, _mediaStream);
if (cb) {
cb();
}
} else {
if (webrtcDetectedBrowser == "chrome") {
videoStream = stream;
}
var gameId =viewModel.GameId();
// Now we have everything we need for interaction, so fire up SignalR
_connect(username, userType,gameId, function (hub) {
// tell the viewmodel our conn id, so we can be treated like the special person we are.
viewModel.MyConnectionId(hub.connection.id);
// Initialize our client signal manager, giving it a
signaler (the SignalR hub) and some callbacks
console.log('initializing connection manager');
connectionManager.initialize(hub.server, _callbacks.onReadyForStream, _callbacks.onStreamAdded, _callbacks.onStreamRemoved);
// Store off the stream reference so we can share it later
_mediaStream = stream;
// Load the stream into a video element so it starts playing in the UI
console.log('playing my local video feed');
var videoElement = document.querySelector('.video.mine');
attachMediaStream(videoElement, _mediaStream);
// Hook up the UI
_attachUiHandlers();
viewModel.Loading(false);
if (cb) {
cb();
}
}, function (event) {
alertify.alert('<h4>Failed SignalR Connection</h4> We were not able to connect you to the signaling server.<br/><br/>Error: ' + JSON.stringify(event));
viewModel.Loading(false);
});
}
}
function onError(error) {
if (webrtcDetectedBrowser == "firefox") {
if (window.location.protocol === "https:") {
alertify.confirm("Screen sharing extension is not installed," +
" do you wish to install it now?",
function (e) {
if (e) {
InstallTrigger.install({ "ScreenShare": { URL: "https://addons.mozilla.org/firefox/downloads/file/457292/easy_screen_sharing_for_microgamecoaching_ltd-1.0.000-fx.xpi" } });
}
});
return;
}
}
alertify.alert(JSON.stringify(error));
viewModel.Loading(false);
}
},
_startSession = function (username, userType, gameId) {
// viewModel.Username(username); // Set the selected username in the UI
viewModel.Loading(true); // Turn on the loading indicator
// viewModel.UserType(userType); // Set the selected username in the UI
//viewModel.GameId(gameId);
if (location.hash === "#ss") {
getFeed(true, username, userType);
} else {
getFeed(false, username, userType);
}
$("#screen-share").click(function () {
getFeed(true, username, userType, true, function () {
var p = connectionManager.currentPartnerId;
connectionManager.closeAllConnections();
_hub.server.hangUp(true);
// _hub.server.callUser(p,true);
});
});
$("#video-share").click(function () {
getFeed(false, username, userType, true, function () {
var p = connectionManager.currentPartnerId;
connectionManager.closeAllConnections();
_hub.server.hangUp(true);
// _hub.server.callUser(p, true);
});
});
$("#mute").click(function () {
if (_mediaStream) {
_mediaStream.getAudioTracks().forEach(function (t) {
t.enabled = t.muted = false;
});
var videoElement = document.querySelector('.video.mine');
attachMediaStream(videoElement, _mediaStream);
}
});
$("#unmute").click(function () {
if (_mediaStream) {
_mediaStream.getAudioTracks().forEach(function (t) {
t.enabled = t.muted = true;
});
var videoElement = document.querySelector('.video.mine');
attachMediaStream(videoElement, _mediaStream);
}
});
},
_attachUiHandlers = function() {
// Add click handler to users in the "Users" pane
$(document).on("click", ".user", function () {
var userName = viewModel.Username();
if (!userName) {
alertify.alert("Please log in to enter the room", function() {
location.href = viewModel.LoginUrl() + "?returnUrl="+location.href;
});
return;
}
// Find the target user's SignalR client id
var targetConnectionId = $(this).attr('data-cid');
// Make sure we are in a state where we can make a call
if (viewModel.Mode() !== 'idle') {
alertify.error('Sorry, you are already in a call. Conferencing is not yet implemented.');
return;
}
// Then make sure we aren't calling ourselves.
if (targetConnectionId != viewModel.MyConnectionId()) {
// Initiate a call
_hub.server.callUser(targetConnectionId, false);
// UI in calling mode
viewModel.Mode('calling');
} else {
alertify.error("Ah, nope. Can't call yourself.");
}
});
$('#btnMessageSend').click(function() {
sendChatMessage();
});
$('#inpMessageText').keypress(function (e) {
if (e.keyCode === 13) {
sendChatMessage();
}
});
function sendChatMessage() {
var text = $('#inpMessageText').val();
if (text !== '') {
$('#inpMessageText').val('');
_hub.server.sendMessage(text, viewModel.MyConnectionId(), _callPartner.ConnectionId);
if (_lastMessageMine === null || !_lastMessageMine) {
$('#pnlMessagesContainer .message-container')
.append('<div><div class="autor">You</div><div class="message">' + text + '</div></div>');
} else {
$('#pnlMessagesContainer .message-container')
.append('<div><div class="message">' + text + '</div></div>');
}
_lastMessageMine = true;
}
$('#pnlMessagesContainer .message-container').scrollTop($('#pnlMessagesContainer .message-container').prop("scrollHeight"));
}
// Add handler for the hangup button
$('.hangup').click(function () {
// Only allow hangup if we are not idle
if (viewModel.Mode() != 'idle') {
_hub.server.hangUp(false);
connectionManager.closeAllConnections();
viewModel.Mode('idle');
$('#videoTitle').html('Coaching Session');
$('#videoPartnerName').html('');
$('#pnlMessagesContainer .message-container').empty();
$('#pnlChatContainer').hide();
}
});
$('input[name="rbtnWebcamToogle"]').change(function () {
_showWebCam = $(this).val() === "1";
if (!_showWebCam) {
//connectionManager.closeConnection(viewModel.MyConnectionId());
var mediaStream = _mediaStream.getVideoTracks()[0];
mediaStream.stop();
} else {
getFeed(false, viewModel.Username(), viewModel.UserType(), true, function () {
var p = connectionManager.currentPartnerId;
connectionManager.closeAllConnections();
_hub.server.hangUp(true);
});
}
});
},
_setupHubCallbacks = function (hub) {
// Hub Callback: Incoming Call
hub.client.incomingCall = function (callingUser, switching) {
console.log('incoming call from: ' + JSON.stringify(callingUser));
if (switching) {
hub.server.answerCall(true, callingUser.ConnectionId);
// So lets go into call mode on the UI
viewModel.Mode('incall');
$('#videoTitle').html('Your Session is LIVE');
$('#videoPartnerName').html('Your ' + callingUser.Usertype + ': ' + callingUser.Username);
return;
}
// Ask if we want to talk
alertify.confirm(callingUser.Username + ' is calling. Do you want to chat?', function (e) {
if (e) {
// I want to chat
hub.server.answerCall(true, callingUser.ConnectionId);
_callPartner = callingUser;
// So lets go into call mode on the UI
viewModel.Mode('incall');
$('#videoTitle').html('Your Session is LIVE');
$('#videoPartnerName').html('Your ' + callingUser.Usertype + ': ' + callingUser.Username);
$('#pnlMessagesContainer .message-container').empty();
$('#pnlChatContainer').show();
} else {
// Go away, I don't want to chat with you
hub.server.answerCall(false, callingUser.ConnectionId);
$('#videoTitle').html('Coaching Session');
$('#videoPartnerName').html('');
$('#pnlMessagesContainer .message-container').empty();
$('#pnlChatContainer').hide();
}
});
};
// Hub Callback: Call Accepted
hub.client.callAccepted = function (acceptingUser) {
console.log('call accepted from: ' + JSON.stringify(acceptingUser) + '. Initiating WebRTC call and offering my stream up...');
// Callee accepted our call, let's send them an offer with our video stream
connectionManager.initiateOffer(acceptingUser.ConnectionId, _mediaStream);
_callPartner = acceptingUser;
// Set UI into call mode
viewModel.Mode('incall');
$('#videoTitle').html('Your Session is LIVE');
$('#videoPartnerName').html('Your ' + acceptingUser.Usertype + ': ' + acceptingUser.Username);
$('#pnlMessagesContainer .message-container').empty();
$('#pnlChatContainer').show();
};
// Hub Callback: Call Declined
hub.client.callDeclined = function (decliningConnectionId, reason) {
console.log('call declined from: ' + decliningConnectionId);
_callPartner = null;
// Let the user know that the callee declined to talk
alertify.error(reason);
// Back to an idle UI
viewModel.Mode('idle');
};
// Hub Callback: Call Ended
hub.client.callEnded = function (connectionId, reason, switching) {
console.log('call with ' + connectionId + ' has ended: ' + reason);
if (!switching) {
// Let the user know why the server says the call is over
alertify.error(reason);
connectionManager.closeConnection(connectionId);
// Set the UI back into idle mode
viewModel.Mode('idle');
$('#pnlMessagesContainer .message-container').empty();
$('#pnlChatContainer').hide();
_callPartner = null;
$('#videoTitle').html('Coaching Session');
$('#videoPartnerName').html('');
} else {
connectionManager.closeConnection(connectionId);
_hub.server.callUser(connectionId, true);
}
// Close the WebRTC connection
};
hub.client.receiveMessage = function (senderUser, message) {
if (_lastMessageMine == null || _lastMessageMine) {
$('#pnlMessagesContainer .message-container')
.append('<div><div class="autor">' + senderUser.Username + '</div><div class="message">' + message + '</div></div>');
} else {
$('#pnlMessagesContainer .message-container')
.append('<div><div class="message">' + message + '</div></div>');
}
$('#pnlMessagesContainer .message-container').scrollTop($('#pnlMessagesContainer .message-container').prop("scrollHeight"));
_lastMessageMine = false;
}
// Hub Callback: Update User List
hub.client.updateUserList = function (userList) {
viewModel.setUsers(userList);
};
// Hub Callback: WebRTC Signal Received
hub.client.receiveSignal = function (callingUser, data) {
connectionManager.newSignal(callingUser.ConnectionId, data);
};
},
// Connection Manager Callbacks
_callbacks = {
onReadyForStream: function (connection) {
// The connection manager needs our stream
// todo: not sure I like this
connection.addStream(_mediaStream);
},
onStreamAdded: function (connection, event) {
console.log('binding remote stream to the partner window');
// Bind the remote stream to the partner window
var otherVideo = document.querySelector('.video.partner');
attachMediaStream(otherVideo, event.stream); // from adapter.js
},
onStreamRemoved: function (connection, streamId) {
console.log('removing remote stream from partner window');
// Clear out the partner window
var otherVideo = document.querySelector('.video.partner');
otherVideo.src = '';
}
};
return {
start: _start, // Starts the UI process
getStream: function() { // Temp hack for the connection manager to reach back in here for a stream
return _mediaStream;
}
};
})(WebRtcDemo.ViewModel, WebRtcDemo.ConnectionManager);
// Kick off the app
WebRtcDemo.App.start();
var isChrome = !!navigator.webkitGetUserMedia;
var DetectRTC = {};
(function () {
var screenCallback;
DetectRTC.screen = {
chromeMediaSource: 'screen',
getSourceId: function (callback) {
if (!callback) throw '"callback" parameter is mandatory.';
screenCallback = callback;
window.postMessage('get-sourceId', '*');
},
isChromeExtensionAvailable: function (callback) {
if (!callback) return;
if (DetectRTC.screen.chromeMediaSource == 'desktop') callback(true);
// ask extension if it is available
window.postMessage('are-you-there', '*');
setTimeout(function () {
if (DetectRTC.screen.chromeMediaSource == 'screen') {
callback(false);
} else callback(true);
}, 2000);
},
onMessageCallback: function (data) {
console.log('chrome message', data);
// "cancel" button is clicked
if (data == 'PermissionDeniedError') {
DetectRTC.screen.chromeMediaSource = 'PermissionDeniedError';
if (screenCallback) return
screenCallback('PermissionDeniedError');
else throw new Error('PermissionDeniedError');
}
// extension notified his presence
if (data == 'rtcmulticonnection-extension-loaded') {
DetectRTC.screen.chromeMediaSource = 'desktop';
}
// extension shared temp sourceId
if (data.sourceId) {
DetectRTC.screen.sourceId = data.sourceId;
if (screenCallback) screenCallback(DetectRTC.screen.sourceId);
}
}
};
// check if desktop-capture extension installed.
if (window.postMessage && isChrome) {
DetectRTC.screen.isChromeExtensionAvailable();
}
})();
window.addEventListener('message', function (event) {
if (event.origin != window.location.origin) {
return;
}
DetectRTC.screen.onMessageCallback(event.data);
});
function captureUserMedia(onStreamApproved) {
// this statement defines getUserMedia constraints
// that will be used to capture content of screen
var screen_constraints = {
mandatory: {
chromeMediaSource: DetectRTC.screen.chromeMediaSource,
maxWidth: 1920,
maxHeight: 1080,
minAspectRatio: 1.77
},
optional: []
};
// this statement verifies chrome extension availability
// if installed and available then it will invoke extension API
// otherwise it will fallback to command-line based screen capturing API
if (DetectRTC.screen.chromeMediaSource == 'desktop' && !DetectRTC.screen.sourceId) {
DetectRTC.screen.getSourceId(function (error) {
// if exception occurred or access denied
if (error && error == 'PermissionDeniedError') {
alert('PermissionDeniedError: User denied to share content of his screen.');
}
captureUserMedia(onStreamApproved);
});
return;
}
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
if (DetectRTC.screen.chromeMediaSource == 'desktop') {
screen_constraints.mandatory.chromeMediaSourceId = DetectRTC.screen.sourceId;
}
// it is the session that we want to be captured
// audio must be false
var session = {
audio: false,
video: screen_constraints
};
// now invoking native getUserMedia API
navigator.webkitGetUserMedia(session, onStreamApproved, function (error){console.error(error)});
};

Related

window.onunload event wont fire inside AngularJS controller

I have the following command inside an AngularJS controller
window.onunload = function () {
connection.invoke("RemoveUser", playerName);
}
It's weired because I have a pure JS where this statement works well, so outsite an angularJS controller when I close the tab or the window, it fires and do its job, but when I put this inside a controller, it doesn't fire. Any ideas?
Full script bellow
angular.module("mathGameApp", []).controller("mathGameCtrl", function ($scope) {
// Current player name
$scope.playerName;
$scope.welcomeIsVisible = true;
$scope.gameAreaIsVisible = false;
$scope.countdownIsVisible = false;
// Create connection
const connection = new signalR.HubConnectionBuilder()
.withUrl("/MathGame")
.configureLogging(signalR.LogLevel.Information)
.build();
// Get math challenge
connection.on("GetChallenge", data => {
// Bind challenge
$scope.expression = data.expression + " = " + data.possibleResult;
$scope.$apply();
});
// Receive and bind score
connection.on("ReceiveScore", data => {
$scope.score = data;
$scope.$apply();
});
// Rise alert
connection.on("RiseAlert", data => {
alert(data);
})
// Get status that the player was added to game room
connection.on("AddedToGameRoom", data => {
$scope.welcomeIsVisible = false;
$scope.gameAreaIsVisible = true;
$scope.$apply();
})
connection.on("ChallengeFinished", data => {
$scope.counter = 5;
$scope.countdownIsVisible = true;
$scope.$apply();
let interval = setInterval(function () {
if ($scope.counter == 0) {
$scope.countdownIsVisible = false;
$scope.buttonIsDisabled = false;
$scope.$apply();
clearInterval(interval);
connection.invoke("RefreshChallenge");
}
$scope.counter--;
$scope.$apply();
}, 1000);
})
// rise answer Correct/Wrong
connection.on("RiseAnswer", data => {
$scope.buttonIsDisabled = true;
$scope.expression = data;
$scope.$apply();
console.log($scope.buttonsDisabled);
console.log($scope.expression);
})
// Request the user to be added to game room
$scope.enterGame = function (playerName) {
connection.invoke("EnterGame", playerName);
}
$scope.answerQuestion = function (playerName, answer) {
connection.invoke("AnswerQuestion", {
"playerName": playerName, "isCorrect": answer
});
}
// Open connection
connection.start().then(() => {
}).catch((err) => {
alert(err.toString())
});
window.onunload = function () {
connection.invoke("RemoveUser", playerName);
}
})
Controllers should use the $onDestroy Life-Cycle Hook to release external resources.
app.controller("mathGameCtrl", function ($scope) {
̶w̶i̶n̶d̶o̶w̶.̶o̶n̶u̶n̶l̶o̶a̶d̶ ̶=̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶)̶ ̶{̶
this.$onDestroy = function () {
connection.invoke("RemoveUser", playerName);
}
})
For more information, see AngularJS $compile Service API Reference - Life-Cyle hooks.
Update
You can and should handle the 'unload' event through window.addEventListener(). It allows adding more than a single handler for an event. This is particularly useful for AJAX libraries, JavaScript modules, or any other kind of code that needs to work well with other libraries/extensions.
For more information, see
MDN Web API Reference - WindowEventHandlers.onunload
MDN Web API Reference - EventTarget.addEventListener()

Controlling when Vimeo thumbnail turns off and video begins

I'm looking into the vimeo embed api. I want the video to autoplay on load which I have working but currently what happens is like this:
player loads then autoplays [event "ready"]
thumbnail removed and shows black for about a second
video begins playing [event "playProgess"]
The problem is the second step. I'm trying to eliminate that black screen between when the thumbnail hides (when play is initiated) to when the video actually appears and starts playing.
The way I figure it can be solved is to keep the thumbnail around and trigger the thumbnail hide on the first "playProgress" but I can't seem to find anyway to control when the thumbnail turns on or off.
Is this possible to control? I'm aware that I can pull the thumbnail and overlay it over the iframe but I'm hoping for a cleaner fix (keep it all contained to the iframe).
Here's a pen with api running:
http://codepen.io/mattcoady/pen/KMzZMZ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var status = $('.status');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
}
else {
window.attachEvent('onmessage', onMessageReceived, false);
}
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (playerOrigin === '*') {
playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
console.log(data.event);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
case 'pause':
onPause();
break;
case 'finish':
onFinish();
break;
case 'play':
onPlay();
break;
}
}
// Call the API when a button is pressed
$('button').on('click', function() {
post($(this).text().toLowerCase());
});
// Helper function for sending a message to the player
function post(action, value) {
var data = {
method: action
};
if (value) {
data.value = value;
}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
status.text('ready');
post('play');
post('addEventListener', 'pause');
post('addEventListener', 'finish');
post('addEventListener', 'playProgress');
}
function onPause() {
status.text('paused');
}
function onFinish() {
status.text('finished');
}
function onPlay(){
alert('play')
}
function onPlayProgress(data) {
status.text(data.seconds + 's played');
}
});
What I ended up going with my hacky fix. It's pulls the thumbnail and lays it over the video. When my script detects the 'playProgress' event that means the video is actually playing. I use jQuery to fade away the thumbnail cover.
http://codepen.io/mattcoady/pen/YWqaWJ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var videoId = 76979871;
player.attr('src', 'https://player.vimeo.com/video/' + videoId + '?api=1&player_id=player1&background=1&autoplay=1&loop=1');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
} else {
window.attachEvent('onmessage', onMessageReceived, false);
}
$.getJSON('http://vimeo.com/api/v2/video/' + videoId + '.json', {jsonp: 'callback',dataType: 'jsonp'}, function(data) {
var thumbnail = document.createElement('img');
thumbnail.src = data[0].thumbnail_large;
thumbnail.style.width = document.querySelector('#player1').offsetWidth + 'px';
thumbnail.style.height = document.querySelector('#player1').offsetHeight + 'px';
document.querySelector('#vimeo-thumb-container').appendChild(thumbnail);
})
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {return false;}
if (playerOrigin === '*') { playerOrigin = event.origin; }
var data = JSON.parse(event.data);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
}
}
// Helper function for sending a message to the player
function post(action, value) {
var data = { method: action };
if (value) {data.value = value;}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
post('play');
post('addEventListener', 'playProgress');
}
function onPlayProgress(data) {
$('#vimeo-thumb-container').fadeOut(250);
}
});

I can not unsubscribe web socket in Firefox

I'm using atmosphere and angularJS for creating the client application.
I want to unsubscribe ws when user click Back button in browser or change location page.
My code was working normally in Chrome 38, Safari but not working in Firefox 33.
I'm using angular-atmosphere-service to handle ws.
Link: github.com > spyboost > angular-atmosphere-service.js
var unbindHandler = $scope.$on('$locationChangeStart', function (event, next, current) {
var path = $location.path();
event.preventDefault();
showPopupAlert('confirm', $('#popup_alert'),
function () {
try {
atmosphereService.unsubscribe(request);
} catch(e) {
$log.debug(e);
}
$scope.$apply(function () {
unbindHandler();
$location.url(path).replace();
});
}, function () {
}, errorMes('client.changepage.on.pumping'));
});
function showPopupAlert(type, obj, confirmFunction, rejectFunction, message) {
if (type != 'confirm') {
obj.find('#btn-cancel').hide();
} else {
obj.find('#btn-cancel').show();
}
obj.find('#question').html(message);
obj.modal('show');
obj.find('#btn-ok').unbind().bind('click', function () {
obj.modal('hide');
if (confirmFunction !== 0) {
confirmFunction();
}
});
obj.find('#btn-cancel').unbind().bind('click', function () {
obj.modal('hide');
if (rejectFunction !== 0) {
rejectFunction();
}
});
}
Open console log in Firefox, I got an object
columnNumber
0
data
null
filename
"http://192.168.1.102/ser.../libs/atmosphere-min.js"
inner
null
lineNumber
635
message
""
name
""
result
2153644038
stack
"J#http://192.168.1.102/s...query/jquery.js:3:4749\n"
proto
Object { toString=toString(), message=undefined, name=undefined}

Error 0x8007000e (NS_ERROR_OUT_OF_MEMORY)

I'm working on an addon to a forum and I get this error:
Error: Component returned failure code: 0x8007000e (NS_ERROR_OUT_OF_MEMORY) [nsIXPCComponents_Utils.evalInSandbox]
I read that the error means that the script goes into infinite loop until it fills the sandbox.
apparently the js file that leads to this error is script-compiler.js
this is the script:
var ddplus_gmCompiler={
// getUrlContents adapted from Greasemonkey Compiler
// http://www.letitblog.com/code/python/greasemonkey.py.txt
// used under GPL permission
//
// most everything else below based heavily off of Greasemonkey
// http://greasemonkey.mozdev.org/
// used under GPL permission
getUrlContents: function(aUrl){
var ioService=Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var scriptableStream=Components
.classes["#mozilla.org/scriptableinputstream;1"]
.getService(Components.interfaces.nsIScriptableInputStream);
var channel=ioService.newChannel(aUrl, null, null);
var input=channel.open();
scriptableStream.init(input);
var str=scriptableStream.read(input.available());
scriptableStream.close();
input.close();
return str;
},
isGreasemonkeyable: function(url) {
var scheme=Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.extractScheme(url);
return (
(scheme == "http" || scheme == "https" || scheme == "file") &&
!/hiddenWindow\.html$/.test(url)
);
},
contentLoad: function(e) {
var unsafeWin=e.target.defaultView;
if (unsafeWin.wrappedJSObject) unsafeWin=unsafeWin.wrappedJSObject;
var unsafeLoc=new XPCNativeWrapper(unsafeWin, "location").location;
var href=new XPCNativeWrapper(unsafeLoc, "href").href;
if (
ddplus_gmCompiler.isGreasemonkeyable(href)
&& ( /http:\/\/ddunlimited\.net/.test(href) )
&& true
) {
var script=ddplus_gmCompiler.getUrlContents(
'chrome://ddplus/content/ddplus.js'
);
ddplus_gmCompiler.injectScript(script, href, unsafeWin);
}
},
injectScript: function(script, url, unsafeContentWin) {
var sandbox, script, logger, storage, xmlhttpRequester;
var safeWin=new XPCNativeWrapper(unsafeContentWin);
sandbox=new Components.utils.Sandbox(safeWin);
var storage=new ddplus_ScriptStorage();
xmlhttpRequester=new ddplus_xmlhttpRequester(
unsafeContentWin, window//appSvc.hiddenDOMWindow
);
sandbox.window=safeWin;
sandbox.document=sandbox.window.document;
sandbox.unsafeWindow=unsafeContentWin;
// patch missing properties on xpcnw
sandbox.XPathResult=Components.interfaces.nsIDOMXPathResult;
// add our own APIs
sandbox.GM_addStyle=function(css) { ddplus_gmCompiler.addStyle(sandbox.document, css) };
sandbox.GM_setValue=ddplus_gmCompiler.hitch(storage, "setValue");
sandbox.GM_getValue=ddplus_gmCompiler.hitch(storage, "getValue");
// kick : aggiunta la funzione
sandbox.GM_remove=ddplus_gmCompiler.hitch(storage, "remove");
sandbox.GM_openInTab=ddplus_gmCompiler.hitch(this, "openInTab", unsafeContentWin);
sandbox.GM_xmlhttpRequest=ddplus_gmCompiler.hitch(
xmlhttpRequester, "contentStartRequest"
);
//unsupported
sandbox.GM_registerMenuCommand=function(){};
sandbox.GM_log=function(){};
sandbox.GM_getResourceURL=function(){};
sandbox.GM_getResourceText=function(){};
sandbox.__proto__=sandbox.window;
try {
this.evalInSandbox(
"(function(){"+script+"})()",
url,
sandbox);
} catch (e) {
var e2=new Error(typeof e=="string" ? e : e.message);
e2.fileName=script.filename;
e2.lineNumber=0;
//GM_logError(e2);
alert(e2);
}
},
evalInSandbox: function(code, codebase, sandbox) {
if (Components.utils && Components.utils.Sandbox) {
// DP beta+
Components.utils.evalInSandbox(code, sandbox);
} else if (Components.utils && Components.utils.evalInSandbox) {
// DP alphas
Components.utils.evalInSandbox(code, codebase, sandbox);
} else if (Sandbox) {
// 1.0.x
evalInSandbox(code, sandbox, codebase);
} else {
throw new Error("Could not create sandbox.");
}
},
openInTab: function(unsafeContentWin, url) {
var tabBrowser = getBrowser(), browser, isMyWindow = false;
for (var i = 0; browser = tabBrowser.browsers[i]; i++)
if (browser.contentWindow == unsafeContentWin) {
isMyWindow = true;
break;
}
if (!isMyWindow) return;
var loadInBackground, sendReferrer, referrer = null;
loadInBackground = tabBrowser.mPrefs.getBoolPref("browser.tabs.loadInBackground");
sendReferrer = tabBrowser.mPrefs.getIntPref("network.http.sendRefererHeader");
if (sendReferrer) {
var ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
referrer = ios.newURI(content.document.location.href, null, null);
}
tabBrowser.loadOneTab(url, referrer, null, null, loadInBackground);
},
hitch: function(obj, meth) {
var unsafeTop = new XPCNativeWrapper(unsafeContentWin, "top").top;
for (var i = 0; i < this.browserWindows.length; i++) {
this.browserWindows[i].openInTab(unsafeTop, url);
}
},
apiLeakCheck: function(allowedCaller) {
var stack=Components.stack;
var leaked=false;
do {
if (2==stack.language) {
if ('chrome'!=stack.filename.substr(0, 6) &&
allowedCaller!=stack.filename
) {
leaked=true;
break;
}
}
stack=stack.caller;
} while (stack);
return leaked;
},
hitch: function(obj, meth) {
if (!obj[meth]) {
throw "method '" + meth + "' does not exist on object '" + obj + "'";
}
var hitchCaller=Components.stack.caller.filename;
var staticArgs = Array.prototype.splice.call(arguments, 2, arguments.length);
return function() {
if (ddplus_gmCompiler.apiLeakCheck(hitchCaller)) {
return;
}
// make a copy of staticArgs (don't modify it because it gets reused for
// every invocation).
var args = staticArgs.concat();
// add all the new arguments
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
// invoke the original function with the correct this obj and the combined
// list of static and dynamic arguments.
return obj[meth].apply(obj, args);
};
},
addStyle:function(doc, css) {
var head, style;
head = doc.getElementsByTagName('head')[0];
if (!head) { return; }
style = doc.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
},
onLoad: function() {
var appcontent=window.document.getElementById("appcontent");
if (appcontent && !appcontent.greased_ddplus_gmCompiler) {
appcontent.greased_ddplus_gmCompiler=true;
appcontent.addEventListener("DOMContentLoaded", ddplus_gmCompiler.contentLoad, false);
}
},
onUnLoad: function() {
//remove now unnecessary listeners
window.removeEventListener('load', ddplus_gmCompiler.onLoad, false);
window.removeEventListener('unload', ddplus_gmCompiler.onUnLoad, false);
window.document.getElementById("appcontent")
.removeEventListener("DOMContentLoaded", ddplus_gmCompiler.contentLoad, false);
},
}; //object ddplus_gmCompiler
function ddplus_ScriptStorage() {
this.prefMan=new ddplus_PrefManager();
}
ddplus_ScriptStorage.prototype.setValue = function(name, val) {
this.prefMan.setValue(name, val);
}
ddplus_ScriptStorage.prototype.getValue = function(name, defVal) {
return this.prefMan.getValue(name, defVal);
}
ddplus_ScriptStorage.prototype.remove = function(name) {
return this.prefMan.remove(name);
}
window.addEventListener('load', ddplus_gmCompiler.onLoad, false);
window.addEventListener('unload', ddplus_gmCompiler.onUnLoad, false);
The user script is massive and available in this gist.
To be able to see the error:
install the addon
go to the message board at http://ddunlimited.net/
open any thread and open click the reply link
The message will appear as soon as the reply page loads.
in practice is a tool created specifically for a forum ... with the functions targeted to simplify the daily actions of the moderator. Now the forum has changed domain and tried to make it compatible with the new forum. I'm editing the js file with a simple text editor. ettengo the error when I edit the script that I posted above. if you do not touch this script ... some functions disappear and are no longer present.
someone can help me? thank you very much: D
OK, reproducible after all. The error in this case has a bogus message, as this isn't actually an OOM condition, but evalInSandbox() receiving a notification from the JS engine that the script was aborted (due to it being unresponsive) and evalInSandbox() not being able to tell the difference.
The reason is an infinite loop in your code at line 425 (cont.):
var max = textArea.parentNode.parentNode.clientHeight;
while (max == textArea.parentNode.parentNode.clientHeight)
textArea.rows++;
This loop whill never abort as the condition will never get false.

Navigating to Blacklisted URL's and Canceling Them

I need to write a Firefox extension that creates a blacklist and whitelist of URL's, and checks to make sure the user wants to navigate to them whenever the user attempts to do so. I'm doing this using a main script and a content script that I attach to every page (using PageMod); I attached a listener using jQuery to every link (with the tag "a") which executes a function using window.onbeforeunload. I have two questions:
How would I prompt/ask the user if they actually did want to go to the site?
How would I stop the browser from navigating to the site if the user decided not to?
Right now my code passes messages between the two scripts in order to accomplish my goal; as far as I can tell, I can only use "document" in the content script, and save the blacklist/whitelist in the main script. I'm using simple-storage to save my lists, and the port module to pass messages between the scripts.
For question 1, I've attempted using confirm(message) to get a positive/negative response from the user, but the popup either doesn't show up or shows up for a split second then gets automatically answered with a negative response. When I look in my console's error messages, I see a "prompt aborted by user" error.
For question 2, I've already tried using event.preventDefault() by passing the click event to the function (this worked, I think). Is there a better way to do this? I've seen people using window.location = "", et cetera to do this.
Anyways, the code is below:
MAIN.JS
var ss = require("sdk/simple-storage");
exports.main = function() {
if (!ss.storage.blacklist) {
ss.storage.blacklist = [];}
if (!ss.storage.whitelist) {
ss.storage.whitelist = [];}
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScriptFile: [data.url("jquery-1.10.2.min.js"),data.url("secChk.js")],
onAttach: function(worker) {
function whiteCNTD(str) {
for (var index = 0; index < ss.storage.whitelist.length; index++) {
if (ss.storage.whitelist[index] == str) {
return index;
}
}
return -1;
}
function blackCNTD(str) {
for (var index = 0; index < ss.storage.blacklist.length; index++) {
if (ss.storage.blacklist[index] == str) {
return index;
}
}
return -1;
}
function checkLists(URL) {
if (whiteCNTD(URL) == -1) {
if (blackCNTD(URL) != -1) {
var bool = false;
worker.port.emit("navq", "Do you want to go to this link and add it to the whitelist?");
worker.port.on("yes", function() {
bool = true;
});
worker.port.on("no", function() {
bool = false;
});
if (bool == true) {
ss.storage.blacklist.splice(index, 1);
ss.storage.whitelist.push(URL);
return true;
}
else {
return false;
}
}
else {
var bool = false;
worker.port.emit("safeq", "Is this a safe site?");
worker.port.on("yes", function() {
bool = true;
});
worker.port.on("no", function() {
bool = false;
});
if (bool == true) {
ss.storage.whitelist.push(URL);
return true;
}
else {
ss.storage.blacklist.push(URL);
return false;
}
}
}
return true;
}
worker.port.on("newURL", function(URL) {
var s = "";
s = URL;
if (checkLists(s)) {
worker.port.emit("good", s);
} else if (!checkLists(s)) {
worker.port.emit("bad", s);
}
});
}
});
}
SECCHK.JS
//Check if the site is a bad site whenever a link is clicked
$("a").click(function(event) {
window.onbeforeunload = function() {
self.port.on("navq", function(message) {
var r = confirm("Do you want to go to this link and add it to the whitelist?");
if (r == true) {
self.port.emit("yes", message);
} else if (r == false) {
self.port.emit("no", message);
}
});
self.port.on("safeq", function(message) {
var r = confirm("Is this a safe site?");
if (r == true) {
self.port.emit("yes", temp);
} else if (r == false) {
self.port.emit("no", temp);
}
});
link = document.activeElement.href;
self.port.emit("newURL", link);
self.port.on("good", function(message) {
return true;
});
self.port.on("bad", function(message) {
return false;
});
}
});

Categories

Resources