www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 56195558667e0dc9a6dda85f7bf2213b58e45162
parent 6a4bcc683e1b1231f36786d497eda615ab430380
Author: Simon Kornblith <simon@simonster.com>
Date:   Fri, 10 Feb 2012 19:10:43 -0500

Improve our chances of getting attachments from the connector. We attach cookies sent from the connector to attachment downloads, which should get most proxies working, provided that the user is logged in. We also use WeakMaps to keep track of active CookieSandboxes, rather than manually removing them from a list.

Diffstat:
Mchrome/content/zotero/xpcom/attachments.js | 6++++--
Mchrome/content/zotero/xpcom/connector/translate_item.js | 19+++++++++++++++----
Mchrome/content/zotero/xpcom/cookieSandbox.js | 130+++++++++++++++++++++++++------------------------------------------------------
Mchrome/content/zotero/xpcom/http.js | 8+++++---
Mchrome/content/zotero/xpcom/mime.js | 4++--
Mchrome/content/zotero/xpcom/server_connector.js | 10++++------
Mchrome/content/zotero/xpcom/translation/translate.js | 3++-
Mchrome/content/zotero/xpcom/translation/translate_item.js | 10+++++++---
8 files changed, 80 insertions(+), 110 deletions(-)

diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js @@ -200,7 +200,7 @@ Zotero.Attachments = new function(){ function importFromURL(url, sourceItemID, forceTitle, forceFileBaseName, parentCollectionIDs, - mimeType, libraryID, callback) { + mimeType, libraryID, callback, cookieSandbox) { Zotero.debug('Importing attachment from URL'); if (sourceItemID && parentCollectionIDs) { @@ -222,6 +222,7 @@ Zotero.Attachments = new function(){ // Save using a hidden browser var nativeHandlerImport = function () { var browser = Zotero.Browser.createHiddenBrowser(); + if(cookieSandbox) cookieSandbox.attachToBrowser(browser); var imported = false; var onpageshow = function() { // ignore spurious about:blank loads @@ -263,6 +264,7 @@ Zotero.Attachments = new function(){ .classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] .createInstance(nsIWBP); wbp.persistFlags = nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; + if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(wbp); var encodingFlags = false; Zotero.DB.beginTransaction(); @@ -396,7 +398,7 @@ Zotero.Attachments = new function(){ else { Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) { process(mimeType, hasNativeHandler); - }); + }, cookieSandbox); } } diff --git a/chrome/content/zotero/xpcom/connector/translate_item.js b/chrome/content/zotero/xpcom/connector/translate_item.js @@ -1,7 +1,7 @@ /* ***** BEGIN LICENSE BLOCK ***** - Copyright © 2009 Center for History and New Media + Copyright © 2012 Center for History and New Media George Mason University, Fairfax, Virginia, USA http://zotero.org @@ -23,10 +23,15 @@ ***** END LICENSE BLOCK ***** */ -Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) { +Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType, document, + cookieSandbox) { this.newItems = []; - this._timeoutID = null; + + if(document) { + this._uri = document.location.toString(); + this._cookie = document.cookie; + } } Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0; @@ -40,7 +45,13 @@ Zotero.Translate.ItemSaver.prototype = { "saveItems":function(items, callback) { var me = this; // first try to save items via connector - Zotero.Connector.callMethod("saveItems", {"items":items}, function(success, status) { + var payload = {"items":items}; + if(this._uri && this._cookie) { + payload.uri = this._uri; + payload.cookie = this._cookie; + } + + Zotero.Connector.callMethod("saveItems", payload, function(success, status) { if(success !== false) { Zotero.debug("Translate: Save via Standalone succeeded"); callback(true, items); diff --git a/chrome/content/zotero/xpcom/cookieSandbox.js b/chrome/content/zotero/xpcom/cookieSandbox.js @@ -27,16 +27,12 @@ * Manage cookies in a sandboxed fashion * * @constructor - * @param {browser} browser Hidden browser object + * @param {browser} [browser] Hidden browser object * @param {String|nsIURI} uri URI of page to manage cookies for (cookies for domains that are not * subdomains of this URI are ignored) * @param {String} cookieData Cookies with which to initiate the sandbox */ Zotero.CookieSandbox = function(browser, uri, cookieData) { - this._webNav = browser.webNavigation; - this._browser = browser; - this._watchedBrowsers = [browser]; - this._watchedXHRs = []; this._observerService = Components.classes["@mozilla.org/observer-service;1"]. getService(Components.interfaces.nsIObserverService); @@ -58,31 +54,14 @@ Zotero.CookieSandbox = function(browser, uri, cookieData) { } } - // register with observer - Zotero.CookieSandbox.Observer.register(this); + if(browser) { + this.attachToBrowser(browser); + } + Zotero.CookieSandbox.Observer.register(); } Zotero.CookieSandbox.prototype = { /** - * Check whether we track a browser for this document - */ - "isDocumentTracked":function(doc) { - var i = this._watchedBrowsers.length; - while(i--) { - var browser = this._watchedBrowsers[i]; - if(doc == browser.contentDocument) return true; - } - return false; - }, - - /** - * Check whether we track an XHR for this document - */ - "isXHRTracked":function(xhr) { - return this._watchedXHRs.indexOf(xhr) !== -1; - }, - - /** * Adds cookies to this CookieSandbox based on a cookie header * @param {String} cookieString; */ @@ -108,27 +87,19 @@ Zotero.CookieSandbox.prototype = { }, /** - * Attach CookieSandbox to a specific XMLHttpRequest - * @param {XMLHttpRequest} xhr + * Attach CookieSandbox to a specific browser + * @param {Browser} browser */ "attachToBrowser":function(browser) { - this._watchedBrowsers.push(browser); + Zotero.CookieSandbox.Observer.trackedBrowsers.set(browser, this); }, /** * Attach CookieSandbox to a specific XMLHttpRequest - * @param {XMLHttpRequest} xhr - */ - "attachToXHR": function(xhr) { - this._watchedXHRs.push(xhr); - }, - - /** - * Destroys this CookieSandbox (intended to be executed when the browser is destroyed) + * @param {nsIInterfaceRequestor} ir */ - "destroy": function() { - // unregister with observer - Zotero.CookieSandbox.Observer.unregister(this); + "attachToInterfaceRequestor": function(ir) { + Zotero.CookieSandbox.Observer.trackedInterfaceRequestors.set(ir.QueryInterface(Components.interfaces.nsIInterfaceRequestor), this); } } @@ -144,36 +115,21 @@ Zotero.CookieSandbox.Observer = new function() { var observerService = Components.classes["@mozilla.org/observer-service;1"]. getService(Components.interfaces.nsIObserverService), - observing = false, - cookieSandboxes = []; + observing = false; + + this.trackedBrowsers = new WeakMap(); + this.trackedInterfaceRequestors = new WeakMap(); /** * Registers cookie manager and observer, if necessary */ this.register = function(CookieSandbox) { - cookieSandboxes.push(CookieSandbox); - if(!observing) { Zotero.debug("CookieSandbox: Registering observers"); for each(var topic in observeredTopics) observerService.addObserver(this, topic, false); observing = true; } - } - - /** - * Unregisters cookie manager and observer - */ - this.unregister = function(CookieSandbox) { - // remove cookie manager from list - cookieSandboxes.splice(cookieSandboxes.indexOf(CookieSandbox), 1); - - // remove observer if this is the last and this is not translation-server - if(cookieSandboxes.length === 0 && !Zotero.isServer) { - Zotero.debug("CookieSandbox: Unregistering observers"); - for each(var topic in observeredTopics) observerService.removeObserver(this, topic); - observing = false; - } - } + }; /** * Implements nsIObserver to watch for new cookies and to add sandboxed cookies @@ -185,50 +141,48 @@ Zotero.CookieSandbox.Observer = new function() { } channel.QueryInterface(Components.interfaces.nsIHttpChannel); - var trackedBy, tested, doc, xhr, + var trackedBy, tested, browser, callbacks, channelURI = channel.URI.spec, notificationCallbacks = channel.notificationCallbacks; - // try the document - try { - doc = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document; - } catch(e) {} - if(doc) { + // try the notification callbacks + trackedBy = this.trackedInterfaceRequestors.get(notificationCallbacks); + if(trackedBy) { tested = true; - for(var i=0, n=cookieSandboxes.length; i<n; i++) { - if(cookieSandboxes[i].isDocumentTracked(doc)) { - trackedBy = cookieSandboxes[i]; - } - } } else { - // try the document for the load group + // try the browser try { - doc = channel.loadGroup.notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document; + browser = notificationCallbacks.getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell).chromeEventHandler; } catch(e) {} - if(doc) { + if(browser) { tested = true; - for(var i=0, n=cookieSandboxes.length; i<n; i++) { - if(cookieSandboxes[i].isDocumentTracked(doc)) { - trackedBy = cookieSandboxes[i]; - } - } + trackedBy = this.trackedBrowsers.get(browser); } else { - // try getting as an XHR + // try the document for the load group try { - xhr = notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest); + browser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell).chromeEventHandler; } catch(e) {} - if(xhr) { + if(browser) { tested = true; - for(var i=0, n=cookieSandboxes.length; i<n; i++) { - if(cookieSandboxes[i].isXHRTracked(xhr)) { - trackedBy = cookieSandboxes[i]; - } + trackedBy = this.trackedBrowsers.get(browser); + } else { + // try getting as an XHR or nsIWBP + try { + notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest); + tested = true; + } catch(e) {} + if(!tested) { + try { + notificationCallbacks.QueryInterface(Components.interfaces.nsIWebBrowserPersist); + tested = true; + } catch(e) {} } } } } - // isTracked is now either true, false, or null // trackedBy => we should manage cookies for this request // tested && !trackedBy => we should not manage cookies for this request // !tested && !trackedBy => this request is of a type we couldn't match to this request. @@ -256,7 +210,6 @@ Zotero.CookieSandbox.Observer = new function() { } // add cookies to be sent to this domain - Zotero.debug(trackedBy.cookieString); channel.setRequestHeader("Cookie", trackedBy.cookieString, false); Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5); } else if(topic == "http-on-examine-response") { @@ -276,7 +229,6 @@ Zotero.CookieSandbox.Observer = new function() { } // put new cookies into our sandbox - Zotero.debug(cookieHeader); if(cookieHeader) trackedBy.addCookiesFromHeader(cookieHeader); Zotero.debug("CookieSandbox: Slurped cookies from "+channelURI, 5); diff --git a/chrome/content/zotero/xpcom/http.js b/chrome/content/zotero/xpcom/http.js @@ -52,7 +52,7 @@ Zotero.HTTP = new function() { _stateChange(xmlhttp, onDone, responseCharset); }; - if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp); + if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp); xmlhttp.send(null); return xmlhttp; @@ -151,7 +151,7 @@ Zotero.HTTP = new function() { _stateChange(xmlhttp, onDone, responseCharset); }; - if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp); + if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp); xmlhttp.send(body); return xmlhttp; @@ -163,9 +163,10 @@ Zotero.HTTP = new function() { * @param {String} url URL to request * @param {Function} onDone Callback to be executed upon request completion * @param {Object} requestHeaders HTTP headers to include with request + * @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object * @return {Boolean} True if the request was sent, or false if the browser is offline */ - this.doHead = function(url, onDone, requestHeaders) { + this.doHead = function(url, onDone, requestHeaders, cookieSandbox) { if (url instanceof Components.interfaces.nsIURI) { // Don't display password in console var disp = url.clone(); @@ -229,6 +230,7 @@ Zotero.HTTP = new function() { _stateChange(xmlhttp, onDone); }; + if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp); xmlhttp.send(null); return xmlhttp; diff --git a/chrome/content/zotero/xpcom/mime.js b/chrome/content/zotero/xpcom/mime.js @@ -280,7 +280,7 @@ Zotero.MIME = new function(){ } - this.getMIMETypeFromURL = function (url, callback) { + this.getMIMETypeFromURL = function (url, callback, cookieSandbox) { Zotero.HTTP.doHead(url, function(xmlhttp) { if (xmlhttp.status != 200 && xmlhttp.status != 204) { Zotero.debug("Attachment HEAD request returned with status code " @@ -308,7 +308,7 @@ Zotero.MIME = new function(){ var hasNativeHandler = Zotero.MIME.hasNativeHandler(mimeType, ext) callback(mimeType, hasNativeHandler); - }); + }, undefined, cookieSandbox); } diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js @@ -156,7 +156,6 @@ Zotero.Server.Connector.Detect.prototype = { } this.sendResponse(200, "application/json", JSON.stringify(jsons)); - this._translate.cookieSandbox.destroy(); Zotero.Browser.deleteHiddenBrowser(this._browser); } } @@ -225,7 +224,6 @@ Zotero.Server.Connector.SavePage.prototype = { "_translatorsAvailable":function(translate, translators) { // make sure translatorsAvailable succeded if(!translators.length) { - me._translate.cookieSandbox.destroy(); Zotero.Browser.deleteHiddenBrowser(this._browser); this.sendResponse(500); return; @@ -251,7 +249,6 @@ Zotero.Server.Connector.SavePage.prototype = { jsonItems.push(jsonItem); }); translate.setHandler("done", function(obj, item) { - me._translate.cookieSandbox.destroy(); Zotero.Browser.deleteHiddenBrowser(me._browser); if(jsonItems.length || me.selectedItems === false) { me.sendResponse(201, "application/json", JSON.stringify({"items":jsonItems})); @@ -296,9 +293,12 @@ Zotero.Server.Connector.SaveItem.prototype = { var collection = zp.getSelectedCollection(); } catch(e) {} + var cookieSandbox = data["uri"] && data["cookie"] ? new Zotero.CookieSandbox(null, data["uri"], + data["cookie"]) : null; + // save items var itemSaver = new Zotero.Translate.ItemSaver(libraryID, - Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1); + Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1, undefined, cookieSandbox); itemSaver.saveItems(data.items, function(returnValue, data) { if(returnValue) { try { @@ -390,8 +390,6 @@ Zotero.Server.Connector.SaveSnapshot.prototype = { // remove browser Zotero.Browser.deleteHiddenBrowser(browser); - // destroy cookieSandbox - cookieSandbox.destroy(); sendResponseCallback(201); } catch(e) { sendResponseCallback(500); diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js @@ -1437,7 +1437,8 @@ Zotero.Translate.Web.prototype._getParameters = function() { return [this.docume */ Zotero.Translate.Web.prototype._prepareTranslation = function() { this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID, - Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1); + Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1, + this.document, this._cookieSandbox); this.newItems = []; } diff --git a/chrome/content/zotero/xpcom/translation/translate_item.js b/chrome/content/zotero/xpcom/translation/translate_item.js @@ -1,7 +1,7 @@ /* ***** BEGIN LICENSE BLOCK ***** - Copyright © 2009 Center for History and New Media + Copyright © 2012 Center for History and New Media George Mason University, Fairfax, Virginia, USA http://zotero.org @@ -23,7 +23,8 @@ ***** END LICENSE BLOCK ***** */ -Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) { +Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType, document, + cookieSandbox) { // initialize constants this.newItems = []; this.newCollections = []; @@ -65,6 +66,7 @@ Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) { // force tag types if requested this._forceTagType = forceTagType; + this._cookieSandbox = cookieSandbox; }; Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0; @@ -346,7 +348,9 @@ Zotero.Translate.ItemSaver.prototype = { var fileBaseName = Zotero.Attachments.getFileBaseNameFromItem(parentID); try { - Zotero.Attachments.importFromURL(attachment.url, parentID, title, fileBaseName); + Zotero.debug('Importing attachment from URL'); + Zotero.Attachments.importFromURL(attachment.url, parentID, title, + fileBaseName, null, mimeType, this._libraryID, null, this._cookieSandbox); } catch(e) { Zotero.debug("Translate: Error adding attachment "+attachment.url, 2); }