Skip to content

Instantly share code, notes, and snippets.

@romannurik
Created September 24, 2009 05:15
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save romannurik/192538 to your computer and use it in GitHub Desktop.
Save romannurik/192538 to your computer and use it in GitHub Desktop.
A workaround for Cross-domain XHR's not working in Chrome Extensions' content scripts. See http://groups.google.com/group/chromium-extensions/browse_thread/thread/43ec4d383cf8d01d
<!DOCTYPE html>
<html>
<head>
<script src="xhrproxy.js"></script>
<script>
setupXHRProxy();
</script>
</head>
</html>
...
proxyXHR({
method: 'GET',
url: 'http://bar.com/external.js',
onComplete: function(status, data) {
if (status == 200) {
alert(data);
} else {
alert("HTTP Error " + status + " while retrieving data.");
}
}
});
...
...
"background_page": "background.html",
"content_scripts": [
{
"matches": ["http://foo.com/*"],
"js": ["xhrproxy.js", "content-script.js"]
}
],
"permissions": [
"http://bar.com/"
]
...
var XHR_PROXY_PORT_NAME_ = 'XHRProxy_';
/**
* Should be called by the background page.
*/
function setupXHRProxy() {
chrome.extension.onConnect.addListener(function(port) {
if (port.name != XHR_PROXY_PORT_NAME_)
return;
port.onMessage.addListener(function(xhrOptions) {
var xhr = new XMLHttpRequest();
xhr.open(xhrOptions.method || "GET", xhrOptions.url, true);
xhr.onreadystatechange = function() {
if (this.readyState == 4) {
port.postMessage({
status: this.status,
data: this.responseText,
xhr: this
});
}
}
xhr.send();
});
});
}
/**
* Should be called by the content script.
*/
function proxyXHR(xhrOptions) {
xhrOptions = xhrOptions || {};
xhrOptions.onComplete = xhrOptions.onComplete || function(){};
var port = chrome.extension.connect({name: XHR_PROXY_PORT_NAME_});
port.onMessage.addListener(function(msg) {
xhrOptions.onComplete(msg.status, msg.data, msg.xhr);
});
port.postMessage(xhrOptions);
}
@SeanJM
Copy link

SeanJM commented Aug 27, 2015

Updated the script to work with manifest version 2
background.js

chrome.extension.onConnect.addListener(function(port) {
  if (port.name != 'XHRProxy_')
    return;
  port.onMessage.addListener(function(xhrOptions) {
    var xhr = new XMLHttpRequest();
    xhr.open(xhrOptions.method || "GET", xhrOptions.url, true);
    xhr.onreadystatechange = function() {
      if (this.readyState == 4) {
        port.postMessage({
          status : this.status,
          data   : this.responseText,
          xhr    : this
        });
      }
    }
    xhr.send();
  });
});

xhrproxy.js
The big change here is to the interface. It makes it so you can do proxyXHR.get('domain').onSuccess(callback) for a bit of a cleaner interface.

var proxyXHR = {};

proxyXHR.get = function (url) {
  var port     = chrome.extension.connect({ name: 'XHRProxy_' });
  var settings = {
    method : 'GET',
    url    : url
  };
  var onSuccess;
  var onFailure;
  var self = {
    onSuccess: function (callback) {
      onSuccess = callback;
      return self;
    },
    onFailure: function (callback) {
      onFailure = callback;
      return self;
    }
  };
  port.onMessage.addListener(function (msg) {
    if (msg.status === 200 && typeof onSuccess === 'function') {
      onSuccess(msg.data, msg.xhr);
    } else if (typeof onFailure === 'function') {
      onFailure(msg.data, msg.xhr);
    }
  });
  port.postMessage(settings);
  return self;
};

content-script.js

proxyXHR.get('http://bar.com/external.js').onSuccess(function (data) {
  alert(data);
}).onFailure(function (status) {
  alert("HTTP Error " + status + " while retrieving data.");
});
"background": {
  "scripts": ["background.js"]
},
"content_scripts": [{
  "matches" : ["http://*/*","https://*/*"],
  "js"      : ["xhrproxy.js", "content-script.js"],
}],

@eComEvo
Copy link

eComEvo commented Sep 12, 2015

@SeanJM The msg.status value is always 0 and the msg.data value is always empty in your revision. Any ideas?

@SeanJM
Copy link

SeanJM commented Sep 16, 2015

Hey eComEvo, If it didn't work I would not have posted it, I am currently using that code in a Chrome plugin with no problems.

How did you structure it?

@aaronmw
Copy link

aaronmw commented Apr 11, 2016

I'm not having any success with this either in the latest version of Chrome. I can't even get onMessage to fire?

@Nou4r
Copy link

Nou4r commented Aug 18, 2016

Hi, do you know why it doesn't work on my website?
http://nou4r.net/x/play/play.php?url=https://ok.ru/video/37675534991

function checkReq(){ var list = document.getElementById("gkpluginsExtListReq"); if(list===null){ return; } list.title = "ready"; if(list.childNodes.length>0){ var curReq = list.firstChild; if(typeof curReq.innerHTML=="undefined"){ list.removeChild(curReq); return; } var obj = JSON.parse(atob(curReq.innerHTML)); obj.onload = obj.onerror = obj.onabor = function(response){ var txtout = document.createElement("textarea"); txtout.id = obj.extreqid; txtout.style.display = "none"; var Hfres = response.status+" "+response.statusText+"\r\n"+response.responseHeaders; if(response.finalUrl){ Hfres += "FinalLocation: "+response.finalUrl+"\r\n"; } if(obj.returndtaescape){ txtout.value = escape(Hfres+"\r\n"+response.responseText); }else if(obj.returndtab64){ txtout.value = btoa(Hfres+"\r\n"+response.responseText); }else{ txtout.value = Hfres+"\r\n"+response.responseText; } document.body.appendChild(txtout); }; GM_xmlhttpRequest(obj); list.removeChild(curReq); } } setInterval(checkReq,100);

@Nou4r
Copy link

Nou4r commented Aug 18, 2016

Works fine, if i load it via tampermonkey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment