Tampermonkey's GM_xmlhttpRequest not implementing 'context' property?

4.7k views Asked by At

I have written a UserScript for Greasemonkey (Firefox) and am testing it for compatibility with Chrome's Tampermonkey, and getting errors in the developer console:

Uncaught TypeError: Cannot read property 'profile_url' of undefined
Uncaught TypeError: Cannot read property 'encoded_name' of undefined

The errors seem to be referencing the onreadystatechanged callback of GM_xmlhttpRequest which is called like this:

var flairs = document.querySelectorAll('span.flair');
var steam_re = /(?:(?:https?:\/\/)?www\.)?(?:steam|pc)(?:community\.com\/?(?:(id|profiles)\/?)?|[\s\-_]*id)?[\/:\s\|]*(.{2,}?)(?:[\/|:\-\[(] ?(?:\/?(?:ghost|enforcer|tech|mm|master))+[\[)]?)?$/i

function get_text(e) { return e.innerText || e.textContent; }

function set_text(e, t) {
    if (e.innerText)
        e.innerText = t;
    else
        e.textContent = t;
}

var parser = new DOMParser();

for (var i = 0; i < flairs.length; i++) {
    var text = get_text(flairs[i]);
    var match = steam_re.exec(text);
    if (match == null || match.length < 3)
        continue;
    var type = match[1] || 'id';
    var name = encodeURIComponent(match[2]);
    var url = 'http://steamcommunity.com/' + type + '/' + name;
    var xml_url = url + '?xml=1';
    GM_xmlhttpRequest({
        method: 'GET',
        url: xml_url, // Link to a steam profile with ?xml=1 added
        accept: 'text/xml',
        context: {
            flair_index: i,
            flair_text: text, // textContent of span element
            encoded_name: name,
            profile_url: url, // Link to steam profile
            query_url: xml_url
        },
        onreadystatechange: function(response) {
            if (response.readyState != 4)
                return;
            // Attempt to fall back to alternate forms of context,
            // none of which works. response.context works on Firefox/Greasemonkey.
            var context = response.context || this.context || context;
            var doc = parser.parseFromString(response.responseText, 'text/xml');
            var validProfile = doc.documentElement.nodeName == 'profile';
            var a = document.createElement('a');
            a.href = validProfile ?
                context.profile_url : // TypeError here, context is undefined
                ('http://steamcommunity.com/actions/SearchFriends?K=' + context.encoded_name);
            a.className += (validProfile ? 'steam-profile-link' : 'steam-profile-search-link');
            var a_text = document.createTextNode(context.flair_text);
            a.appendChild(a_text);
            set_text(flairs[context.flair_index], '');
            flairs[context.flair_index].appendChild(a);
        }
    });
}

The function itself is called fine, and the callback is invoked, but once I try to access the context var inside it, it's undefined.

It all works as expected in Firefox. What it does is iterating over span elements that have the "flair" class and checking with a regex if they contain a Steam username, and if so, makes it a link to their SteamCommunity page. (Full source on github). The script runs on /r/PaydayTheHeistOnline.

I have tested using an array defined outside the function to store the data instead of using the context property passed to xmlhttpRequest, but I'm getting the exact same error.

1

There are 1 answers

1
Brock Adams On BEST ANSWER

Update:
Tampermonkey now reports that this feature is fixed as of version 3.8.4116 (in beta at the moment). See:


Older/generic workaround:
The context property is a relatively new feature of GM_xmlhttpRequest(), in Firefox. I doubt it's been implemented in Tampermonkey yet; see Tampermonkey's docs for GM_xmlhttpRequest().

Meanwhile, the tried-and-true method for this kind of thing is to use a closure.

Change your GM_xmlhttpRequest() call to something like:

( function (flair_index, flair_text, encoded_name, profile_url, query_url) {
    GM_xmlhttpRequest ( {
        method: 'GET',
        url:    xml_url, // Link to a steam profile with ?xml=1 added
        accept: 'text/xml',
        onreadystatechange: function (response) {
            if (response.readyState != 4)
                return;

            var doc = parser.parseFromString (response.responseText, 'text/xml');
            var validProfile = doc.documentElement.nodeName == 'profile';
            var a = document.createElement ('a');
            a.href = validProfile ?
                profile_url : 
                ('http://steamcommunity.com/actions/SearchFriends?K=' + encoded_name);
            a.className += (validProfile ? 'steam-profile-link' : 'steam-profile-search-link');
            var a_text = document.createTextNode (flair_text);
            a.appendChild (a_text);
            set_text (flairs[flair_index], '');
            flairs[flair_index].appendChild (a);
        }
    } );
} ) (
    i,
    text, // textContent of span element
    name,
    url, // Link to steam profile
    xml_url
);