Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I play sound with a GreaseMonkey script in FireFox when there's a "Content Security Policy" error?

var audioPlayer = new Audio("http://URLTOMYSOUND/ping.mp3");
audioPlayer.play();

I get this error upon loading the audio:

Content Security Policy: The page's settings blocked the loading of a resource at...

How do I get around this? I don't care where the sound is, I just want to play it. It could be local, but my impression was that local file access would be a no-no as well.

like image 248
Almo Avatar asked Feb 17 '15 03:02

Almo


1 Answers

It is not possible(at the moment) to play a sound from file if you are running into the Content Security Policy(CSP) issues as of Greasemonkey 2.3. If you can modify the HTTP headers of the site you want to run on, it is possible. See Using Content Security Policy and CSP policy directives.

Using GM_xmlhttpRequest which "allows these requests to cross the same origin policy boundaries", does not work because of a bug in the current GM 2.3. See issue #2045. The problem stems from not being able to copy the ArrayBuffer into the sandbox land to get the proper permissions to decode, etc.

The snippet below should work in future GM versions.

// ==UserScript==
// @name        Testing Web Audio: Load from file
// @namespace   http://ericeastwood.com/
// @include     http://example.com/
// @version     1
// @grant       GM_xmlhttpRequest
// ==/UserScript==
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
// Note: this is not an actual reference to a real mp3
var url = 'http://example.com/some.mp3';

var soundLoadedPromise = new Promise(function(resolve, reject) {
    // This will get around the CORS issue
    //      http://wiki.greasespot.net/GM_xmlhttpRequest
    var req = GM_xmlhttpRequest({
        method: "GET",
        url: url,
        responseType: 'arraybuffer',
        onload: function(response) {
            try {
                context.decodeAudioData(response.response, function(buffer) { 
                    resolve(buffer)
                }, function(e) {
                    reject(e);
                }); 
            }
            catch(e) {
                reject(e);
            }
        }
    });
});

soundLoadedPromise.then(function(buffer) {
    playSound(buffer);
}, function(e) {
    console.log(e);
});

function playSound(buffer) {
    // creates a sound source
    var source = context.createBufferSource();
    // tell the source which sound to play
    source.buffer = buffer;
    // connect the source to the context's destination (the speakers)
    source.connect(context.destination);
    // play the source now
    // note: on older systems, may have to use deprecated noteOn(time);
    source.start(0);
}

For now, if you only need some sound, I would just procedurally generate it with an oscillator. The demo below uses AudioContext.createOscillator.

Demo: jsFiddle

// ==UserScript==
// @name        Test Web Audio: Oscillator - Web Audio
// @namespace   http://ericeastwood.com/
// @include     http://example.com/
// @version     1
// @grant       none
// ==/UserScript==
window.AudioContext = window.AudioContext || window.webkitAudioContext;

context = new AudioContext();

var o = context.createOscillator();
o.type = 'sine';
o.frequency.value = 261.63;
o.connect(context.destination);

// Start the sound
o.start(0);
// Play the sound for a second before stopping it
setTimeout(function() {
    o.stop(0);
}, 1000);
like image 197
MLM Avatar answered Sep 30 '22 09:09

MLM