From a7984916ad2fceab5ffd39feba5d2fe44ab70381 Mon Sep 17 00:00:00 2001 From: Simon Kornblith Date: Fri, 24 May 2013 00:39:19 -0400 Subject: [PATCH] Protect against pasting HTML-formatted text into QuickFormat dialog --- chrome/content/zotero/fileInterface.js | 23 +------- .../content/zotero/integration/quickFormat.js | 57 +++++++++++++------ .../zotero/xpcom/utilities_internal.js | 25 ++++++++ 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index 4367a36351..06a48b7c3c 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -241,30 +241,11 @@ var Zotero_File_Interface = new function() { * Imports from clipboard */ this.importFromClipboard = function () { - var clip = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); - if (!clip.hasDataMatchingFlavors(["text/unicode"], 1, clip.kGlobalClipboard)) { + var str = Zotero.Utilities.Internal.getClipboard("text/unicode"); + if(!str) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); ps.alert(null, "", Zotero.getString('fileInterface.importClipboardNoDataError')); - return; - } - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - trans.addDataFlavor("text/unicode"); - clip.getData(trans, clip.kGlobalClipboard); - var str = {}; - try { - trans.getTransferData("text/unicode", str, {}); - str = str.value.QueryInterface(Components.interfaces.nsISupportsString).data; - } - catch (e) { - Zotero.debug(e); - return; - } - if (!str) { - Zotero.debug("No clipboard text to import"); - return; } var translate = new Zotero.Translate.Import(); diff --git a/chrome/content/zotero/integration/quickFormat.js b/chrome/content/zotero/integration/quickFormat.js index d9a9070cd9..f3c0962a1c 100644 --- a/chrome/content/zotero/integration/quickFormat.js +++ b/chrome/content/zotero/integration/quickFormat.js @@ -22,6 +22,7 @@ ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://gre/modules/Services.jsm"); var Zotero_QuickFormat = new function () { const pixelRe = /^([0-9]+)px$/ @@ -36,10 +37,8 @@ var Zotero_QuickFormat = new function () { keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0, separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging, panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator, - panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false; - - // A variable that contains the timeout object for the latest onKeyPress event - var eventTimeout = null; + panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false, + searchTimeout; const SHOWN_REFERENCES = 7; @@ -120,6 +119,7 @@ var Zotero_QuickFormat = new function () { qfb.addEventListener("keypress", _onQuickSearchKeyPress, false); qfe = qfiDocument.getElementById("quick-format-editor"); qfe.addEventListener("drop", _onBubbleDrop, false); + qfe.addEventListener("paste", _onPaste, false); } } @@ -180,11 +180,17 @@ var Zotero_QuickFormat = new function () { var range = selection.getRangeAt(0); var node = range.startContainer; - if(node !== range.endContainer || node.nodeType !== Node.TEXT_NODE ) { - return false; + if(node !== range.endContainer) return false; + if(node.nodeType === Node.TEXT_NODE) return node; + + // Range could be referenced to the body element + if(node === qfe) { + var offset = range.startOffset; + if(offset !== range.endOffset) return false; + node = qfe.childNodes[Math.min(qfe.childNodes.length-1, offset)]; + if(node.nodeType === Node.TEXT_NODE) return node; } - - return node; + return false; } /** @@ -193,7 +199,7 @@ var Zotero_QuickFormat = new function () { */ function _getEditorContent(clear) { var node = _getCurrentEditorTextNode(); - return node ? node.textContent : false; + return node ? node.wholeText : false; } /** @@ -1011,8 +1017,13 @@ var Zotero_QuickFormat = new function () { } /** - * + * Reset timer that controls when search takes place. We use this to avoid searching after each + * keypress, since searches can be slow. */ + function _resetSearchTimer() { + if(searchTimeout) clearTimeout(searchTimeout); + searchTimeout = setTimeout(_quickFormat, 250); + } /** * Handle return or escape @@ -1038,9 +1049,7 @@ var Zotero_QuickFormat = new function () { } _resize(); - - if(Zotero_QuickFormat.eventTimeout) clearTimeout(Zotero_QuickFormat.eventTimeout); - Zotero_QuickFormat.eventTimeout = setTimeout(_quickFormat, 250); + _resetSearchTimer(); } else if(keyCode === event.DOM_VK_LEFT || keyCode === event.DOM_VK_RIGHT) { var right = keyCode === event.DOM_VK_RIGHT, bubble = _getSelectedBubble(right); @@ -1102,9 +1111,7 @@ var Zotero_QuickFormat = new function () { }; } } else { - // Use a timeout so that _quickFormat gets called after update - if(Zotero_QuickFormat.eventTimeout) clearTimeout(Zotero_QuickFormat.eventTimeout); - Zotero_QuickFormat.eventTimeout = setTimeout(_quickFormat, 250); + _resetSearchTimer(); } } @@ -1163,6 +1170,24 @@ var Zotero_QuickFormat = new function () { _moveCursorToEnd(); _showCitationProperties(event.currentTarget); } + + /** + * Called when the user attempts to paste + */ + function _onPaste(event) { + event.stopPropagation(); + event.preventDefault(); + + var str = Zotero.Utilities.Internal.getClipboard("text/unicode"); + if(str) { + var selection = qfiWindow.getSelection(); + var range = selection.getRangeAt(0); + range.deleteContents(); + range.insertNode(document.createTextNode(str.replace(/[\r\n]/g, " ").trim())); + range.collapse(false); + _resetSearchTimer(); + } + } /** * Handle changes to citation properties diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js index 0f2c297450..45e90681be 100644 --- a/chrome/content/zotero/xpcom/utilities_internal.js +++ b/chrome/content/zotero/xpcom/utilities_internal.js @@ -199,6 +199,31 @@ Zotero.Utilities.Internal = { }}); return deferred.promise; + }, + + /** + * Get string data from the clipboard + * @param {String[]} mimeType MIME type of data to get + * @return {String|null} Clipboard data, or null if none was available + */ + "getClipboard":function(mimeType) { + var clip = Services.clipboard; + if (!clip.hasDataMatchingFlavors([mimeType], 1, clip.kGlobalClipboard)) { + return null; + } + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + trans.addDataFlavor(mimeType); + clip.getData(trans, clip.kGlobalClipboard); + var str = {}; + try { + trans.getTransferData(mimeType, str, {}); + str = str.value.QueryInterface(Components.interfaces.nsISupportsString).data; + } + catch (e) { + return null; + } + return str; } }