commit f9a0db633c2fdac65c3a222047697fa175f2b593
parent b7210ec2352f3b5c95f427cb5d5e8b33002f20cb
Author: Simon Kornblith <simon@simonster.com>
Date: Thu, 1 Sep 2011 06:53:20 +0000
IE cross-domain messaging hack
Diffstat:
3 files changed, 111 insertions(+), 20 deletions(-)
diff --git a/chrome/content/zotero/xpcom/connector/connector.js b/chrome/content/zotero/xpcom/connector/connector.js
@@ -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
+ });
+ }
}
}
diff --git a/chrome/content/zotero/xpcom/server.js b/chrome/content/zotero/xpcom/server.js
@@ -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;
}
}
diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js
@@ -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() {};