IE cross-domain messaging hack
This commit is contained in:
parent
b7210ec235
commit
f9a0db633c
3 changed files with 111 additions and 20 deletions
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue