IE cross-domain messaging hack

This commit is contained in:
Simon Kornblith 2011-09-01 06:53:20 +00:00
parent b7210ec235
commit f9a0db633c
3 changed files with 111 additions and 20 deletions

View file

@ -26,7 +26,9 @@
Zotero.Connector = new function() {
const CONNECTOR_URI = "http://127.0.0.1:23119/";
const CONNECTOR_API_VERSION = 2;
const IE_HACK_MSG = "ZOTERO_IE_HACK_MSG ";
var _ieStandaloneIframeTarget;
this.isOnline = null;
/**
@ -34,9 +36,32 @@ Zotero.Connector = new function() {
* @param {Function} callback
*/
this.checkIsOnline = function(callback) {
Zotero.Connector.callMethod("ping", {}, function(status) {
callback(status !== false);
});
// Only check once in bookmarklet
if(Zotero.isBookmarklet && this.isOnline !== null) callback(this.isOnline);
if(Zotero.isIE) {
var listener = function(event) {
if(event.origin === "http://www.zotero.org" && event.data === "ZOTERO_IE_STANDALONE_LOADED false") {
callback(false);
} else if(event.origin === "http://127.0.0.1:23119" && event.data === "ZOTERO_IE_STANDALONE_LOADED true") {
_ieStandaloneIframeTarget = event.source;
callback(true);
} else {
return;
}
window.removeEventListener("message", listener, false);
};
window.addEventListener("message", listener, false);
Zotero.debug("Connector: Trying IE hack");
var s = document.createElement("iframe");
s.src = "http://www.zotero.org/bookmarklet/ie_hack.html";
(document.body ? document.body : document.documentElement).appendChild(s);
} else {
Zotero.Connector.callMethod("ping", {}, function(status) {
callback(status !== false);
});
}
}
// saner descriptions of some HTTP error codes
@ -56,6 +81,9 @@ Zotero.Connector = new function() {
* @param {Function} callback Function to be called when requests complete.
*/
this.callMethod = function(method, data, callback) {
// Don't bother trying if not online in bookmarklet
if(Zotero.isBookmarklet && this.isOnline === false) callback(false, 0);
var newCallback = function(req) {
try {
var isOnline = req.status !== Zotero.Connector.EXCEPTION_NOT_AVAILABLE
@ -98,14 +126,43 @@ Zotero.Connector = new function() {
return;
}
};
var uri = CONNECTOR_URI+"connector/"+method;
Zotero.HTTP.doPost(uri, JSON.stringify(data),
newCallback, {
"Content-Type":"application/json",
"X-Zotero-Version":Zotero.version,
"X-Zotero-Connector-API-Version":CONNECTOR_API_VERSION
});
var uri = CONNECTOR_URI+"connector/"+method;
if(Zotero.isIE) { // IE requires XDR for CORS
if(_ieStandaloneIframeTarget) {
var requestID = Zotero.Utilities.randomString();
var listener = function(event) {
if(event.origin === "http://127.0.0.1:23119" && event.data.substr(0, IE_HACK_MSG.length) === IE_HACK_MSG) {
var data = JSON.parse(event.data.substr(IE_HACK_MSG.length));
if(data[0] !== "connectorResponse" || data[1][0] !== requestID) return;
window.removeEventListener("message", listener, false);
var xhrSurrogate = {
"status":data[1][1],
"responseText":data[1][2],
"getResponseHeader":function(x) { return data[1][3][x] }
};
newCallback(xhrSurrogate);
}
};
window.addEventListener("message", listener, false);
_ieStandaloneIframeTarget.postMessage(IE_HACK_MSG+" "+JSON.stringify(["connectorRequest",
[requestID, method, JSON.stringify(data)]]));
} else {
Zotero.debug("Connector: No iframe target; not sending to Standalone");
callback(false, 0);
}
} else { // Other browsers can use plain doPost
Zotero.HTTP.doPost(uri, JSON.stringify(data),
newCallback, {
"Content-Type":"application/json",
"X-Zotero-Version":Zotero.version,
"X-Zotero-Connector-API-Version":CONNECTOR_API_VERSION
});
}
}
}

View file

@ -65,14 +65,12 @@ Zotero.Server = new function() {
/**
* generates the response to an HTTP request
*/
this.generateResponse = function (status, contentType, body) {
this.generateResponse = function (status, contentType, body, headers) {
var response = "HTTP/1.0 "+status+" "+responseCodes[status]+"\r\n";
if(!Zotero.isServer) {
response += "X-Zotero-Version: "+Zotero.version+"\r\n";
response += "X-Zotero-Connector-API-Version: "+CONNECTOR_API_VERSION+"\r\n";
response += "Access-Control-Allow-Origin: "+ZOTERO_CONFIG.BOOKMARKLET_URL+"iframe.html\r\n";
response += "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n";
response += "Access-Control-Allow-Headers: Content-Type,X-Zotero-Connector-API-Version,X-Zotero-Version\r\n";
if(headers) response += headers;
}
if(body) {
@ -315,11 +313,20 @@ Zotero.Server.DataListener.prototype._bodyData = function() {
*/
Zotero.Server.DataListener.prototype._processEndpoint = function(method, postData) {
try {
var headers = "";
const originRe = /[\r\n]Origin: +([^ \r\n]+)/i;
var m = originRe.exec(this.header);
if(m && m[1] === "https://www.zotero.org" || m[1] === "http://www.zotero.org") { Zotero.debug(m[1]);
headers += "Access-Control-Allow-Origin: "+m[1]+"\r\n";
headers += "Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n";
headers += "Access-Control-Allow-Headers: Content-Type,X-Zotero-Connector-API-Version,X-Zotero-Version\r\n";
}
var endpoint = new this.endpoint;
// check that endpoint supports method
if(endpoint.supportedMethods && endpoint.supportedMethods.indexOf(method) === -1) {
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support method\n"));
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support method\n", headers));
return;
}
@ -328,7 +335,7 @@ Zotero.Server.DataListener.prototype._processEndpoint = function(method, postDat
// check that endpoint supports contentType
var supportedDataTypes = endpoint.supportedDataTypes;
if(supportedDataTypes && supportedDataTypes.indexOf(this.contentType) === -1) {
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support content-type\n"));
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support content-type\n", headers));
return;
}
@ -337,7 +344,7 @@ Zotero.Server.DataListener.prototype._processEndpoint = function(method, postDat
try {
decodedData = JSON.parse(postData);
} catch(e) {
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid JSON provided\n"));
this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid JSON provided\n", headers));
return;
}
} else if(supportedDataTypes && this.contentType === "application/x-www-urlencoded") {
@ -350,7 +357,7 @@ Zotero.Server.DataListener.prototype._processEndpoint = function(method, postDat
// set up response callback
var me = this;
var sendResponseCallback = function(code, contentType, arg) {
me._requestFinished(Zotero.Server.generateResponse(code, contentType, arg));
me._requestFinished(Zotero.Server.generateResponse(code, contentType, arg, headers));
}
// pass to endpoint
@ -366,7 +373,7 @@ Zotero.Server.DataListener.prototype._processEndpoint = function(method, postDat
}
} catch(e) {
Zotero.debug(e);
this._requestFinished(Zotero.Server.generateResponse(500), "text/plain", "An error occurred\n");
this._requestFinished(Zotero.Server.generateResponse(500), "text/plain", "An error occurred\n", headers);
throw e;
}
}

View file

@ -489,7 +489,7 @@ Zotero.Server.Connector.Ping = function() {};
Zotero.Server.Endpoints["/connector/ping"] = Zotero.Server.Connector.Ping;
Zotero.Server.Connector.Ping.prototype = {
"supportedMethods":["POST"],
"supportedDataTypes":["application/json"],
"supportedDataTypes":["application/json", "text/plain"],
/**
* Finishes up translation when item selection is complete
@ -501,6 +501,33 @@ Zotero.Server.Connector.Ping.prototype = {
}
}
/**
* Test connection
*
* Accepts:
* Nothing
* Returns:
* Nothing (200 OK response)
*/
Zotero.Server.Connector.Ping = function() {};
Zotero.Server.Endpoints["/connector/ieHack"] = Zotero.Server.Connector.Ping;
Zotero.Server.Connector.Ping.prototype = {
"supportedMethods":["POST"],
"supportedDataTypes":["application/json"],
/**
* Finishes up translation when item selection is complete
* @param {String} data POST data or GET query string
* @param {Function} sendResponseCallback function to send HTTP response
*/
"init":function(postData, sendResponseCallback) {
sendResponseCallback(200, "text/html",
'<!DOCTYPE html><html><head>'+
'<script src="https://www.zotero.org/bookmarklet/ie_compat.js"></script>'+
'<script src="https://www.zotero.org/bookmarklet/ie_hack.js"></script>'+
'</head><body></body></html>');
}
}
// XXX For compatibility with older connectors; to be removed
Zotero.Server.Connector.IncompatibleVersion = function() {};