commit 0144a6719ccb45ec7727454880c4350e40a418d5 Author: Kathy Brade brade@pearlcrescent.com Date: Thu Jul 20 17:11:53 2017 -0400
fixup! Bug 19273: Avoid JavaScript patching of the external app helper dialog.
For compatibility with recent changes to our browser patch, change the external app blocker module so it implements the new nsIHelperAppWarningDialog interface. Since the external app blocker module is no longer a service, split the drag and drop filter into a separate component (which remains a service). --- src/chrome.manifest | 7 +- src/components/dragDropFilter.js | 86 +++++++++++++++++ src/components/external-app-blocker.js | 169 ++++++++++++++++----------------- 3 files changed, 175 insertions(+), 87 deletions(-)
diff --git a/src/chrome.manifest b/src/chrome.manifest index 272401b..65f55a9 100644 --- a/src/chrome.manifest +++ b/src/chrome.manifest @@ -143,8 +143,11 @@ skin torbutton classic/1.0 chrome/skin/ style chrome://global/content/customizeToolbar.xul chrome://torbutton/skin/torbutton.css
# Firefox 4-style component registration +component {f605ec27-d867-44b5-ad97-2a29276642c3} components/dragDropFilter.js +contract @torproject.org/torbutton-dragDropFilter;1 {f605ec27-d867-44b5-ad97-2a29276642c3} + component {3da0269f-fc29-4e9e-a678-c3b1cafcf13f} components/external-app-blocker.js -contract @torproject.org/torbutton-extAppBlockerService;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f} +contract @torproject.org/torbutton-extAppBlocker;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f}
component {06322def-6fde-4c06-aef6-47ae8e799629} components/startup-observer.js contract @torproject.org/startup-observer;1 {06322def-6fde-4c06-aef6-47ae8e799629} @@ -165,4 +168,4 @@ category profile-after-change CookieJarSelector @torproject.org/cookie-jar-selec
category profile-after-change StartupObserver @torproject.org/startup-observer;1 category profile-after-change DomainIsolator @torproject.org/domain-isolator;1 -category profile-after-change ExtAppBlockerService @torproject.org/torbutton-extAppBlockerService;1 +category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1 diff --git a/src/components/dragDropFilter.js b/src/components/dragDropFilter.js new file mode 100644 index 0000000..22dde86 --- /dev/null +++ b/src/components/dragDropFilter.js @@ -0,0 +1,86 @@ +/************************************************************************* + * Drag and Drop Handler. + * + * Implements an observer that filters drag events to prevent OS + * access to URLs (a potential proxy bypass vector). + *************************************************************************/ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +// Module specific constants +const kMODULE_NAME = "Torbutton Drag and Drop Handler"; +const kCONTRACT_ID = "@torproject.org/torbutton-dragDropFilter;1"; +const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3"); + +const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo]; + +function DragDropFilter() { + this.logger = Cc["@torproject.org/torbutton-logger;1"] + .getService(Ci.nsISupports).wrappedJSObject; + this.logger.log(3, "Component Load 0: New DragDropFilter."); + + try { + var observerService = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + observerService.addObserver(this, "on-datatransfer-available", false); + } catch(e) { + this.logger.log(5, "Failed to register drag observer"); + } +} + +DragDropFilter.prototype = +{ + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), + + // make this an nsIClassInfo object + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: kMODULE_NAME, + contractID: kCONTRACT_ID, + classID: kMODULE_CID, + + // method of nsIClassInfo + getInterfaces: function(count) { + count.value = kInterfaces.length; + return kInterfaces; + }, + + // method of nsIClassInfo + getHelperForLanguage: function(count) { return null; }, + + // method of nsIObserver + observe: function(subject, topic, data) { + if (topic == "on-datatransfer-available") { + this.logger.log(3, "The DataTransfer is available"); + return this.filterDataTransferURLs(subject); + } + }, + + filterDataTransferURLs: function(aDataTransfer) { + var types = null; + var type = ""; + var count = aDataTransfer.mozItemCount; + var len = 0; + for (var i = 0; i < count; ++i) { + this.logger.log(3, "Inspecting the data transfer: " + i); + types = aDataTransfer.mozTypesAt(i); + len = types.length; + for (var j = 0; j < len; ++j) { + type = types[j]; + this.logger.log(3, "Type is: " + type); + if (type == "text/x-moz-url" || + type == "text/x-moz-url-data" || + type == "text/uri-list" || + type == "application/x-moz-file-promise-url") { + aDataTransfer.clearData(type); + this.logger.log(3, "Removing " + type); + } + } + } + } +}; + +var NSGetFactory = XPCOMUtils.generateNSGetFactory([DragDropFilter]); diff --git a/src/components/external-app-blocker.js b/src/components/external-app-blocker.js index e56fed3..ca86580 100644 --- a/src/components/external-app-blocker.js +++ b/src/components/external-app-blocker.js @@ -7,19 +7,24 @@ * Handles displaying confirmation dialogs for external apps and protocols * due to Firefox Bug https://bugzilla.mozilla.org/show_bug.cgi?id=440892 * - * Also implements an observer that filters drag events to prevent OS - * access to URLs (a potential proxy bypass vector). + * An instance of this module is created each time the browser starts to + * download a file that may be opened by another or application and when + * an external application may be invoked to handle an URL (e.g., when the + * user clicks on a mailto: URL). *************************************************************************/
const Cc = Components.classes; const Ci = Components.interfaces; +const Cr = Components.results; const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
// Module specific constants const kMODULE_NAME = "Torbutton External App Handler"; -const kCONTRACT_ID = "@torproject.org/torbutton-extAppBlockerService;1"; +const kCONTRACT_ID = "@torproject.org/torbutton-extAppBlocker;1"; const kMODULE_CID = Components.ID("3da0269f-fc29-4e9e-a678-c3b1cafcf13f");
const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo]; @@ -28,23 +33,14 @@ function ExternalAppBlocker() { this.logger = Cc["@torproject.org/torbutton-logger;1"] .getService(Ci.nsISupports).wrappedJSObject; this.logger.log(3, "Component Load 0: New ExternalAppBlocker."); - - this._prefs = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefBranch); - - try { - var observerService = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - observerService.addObserver(this, "external-app-requested", false); - observerService.addObserver(this, "on-datatransfer-available", false); - } catch(e) { - this.logger.log(5, "Failed to register external app observer or drag observer"); - } }
ExternalAppBlocker.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), + _helperAppLauncher: undefined, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver, + Ci.nsIHelperAppWarningDialog]),
// make this an nsIClassInfo object flags: Ci.nsIClassInfo.DOM_OBJECT, @@ -58,83 +54,86 @@ ExternalAppBlocker.prototype = return kInterfaces; },
- // method of nsIClassInfo + // method of nsIClassInfo getHelperForLanguage: function(count) { return null; },
- // Returns true if launch should proceed. - _confirmLaunch: function() { - if (!this._prefs.getBoolPref("extensions.torbutton.launch_warning")) { - return true; - } - - var wm = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Ci.nsIWindowMediator); - var chrome = wm.getMostRecentWindow("navigator:browser"); - - var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Ci.nsIPromptService); - var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_IS_STRING + - prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_IS_STRING + - prompts.BUTTON_DELAY_ENABLE + - prompts.BUTTON_POS_1_DEFAULT; - - var title = chrome.torbutton_get_property_string("torbutton.popup.external.title"); - var app = chrome.torbutton_get_property_string("torbutton.popup.external.app"); - var note = chrome.torbutton_get_property_string("torbutton.popup.external.note"); - var suggest = chrome.torbutton_get_property_string("torbutton.popup.external.suggest"); - var launch = chrome.torbutton_get_property_string("torbutton.popup.launch"); - var cancel = chrome.torbutton_get_property_string("torbutton.popup.cancel"); - var dontask = chrome.torbutton_get_property_string("torbutton.popup.dontask"); - - var check = {value: false}; - var result = prompts.confirmEx(chrome, title, app+note+suggest+" ", - flags, launch, cancel, "", dontask, check); - - if (check.value) { - this._prefs.setBoolPref("extensions.torbutton.launch_warning", false); + // method of nsIHelperAppWarningDialog + maybeShow: function(aLauncher, aWindowContext) + { + // Hold a reference to the object that called this component. This is + // important not just because we need to later invoke the + // continueRequest() or cancelRequest() callback on aLauncher, but also + // so that the launcher object (which is a reference counted object) is + // not released too soon. + this._helperAppLauncher = aLauncher; + + if (!Services.prefs.getBoolPref("extensions.torbutton.launch_warning")) { + this._helperAppLauncher.continueRequest(); + return; }
- return (0 == result); - }, - - observe: function(subject, topic, data) { - if (topic == "external-app-requested") { - this.logger.log(3, "External app requested"); - // subject.data is true if the launch should be canceled. - if ((subject instanceof Ci.nsISupportsPRBool) - && !subject.data /* not canceled already */ - && !this._confirmLaunch()) { - subject.data = true; // The user said to cancel the launch. - } - } else if (topic == "on-datatransfer-available") { - this.logger.log(3, "The DataTransfer is available"); - return this.filterDataTransferURLs(subject); - } + this._showPrompt(aWindowContext); },
- filterDataTransferURLs: function(aDataTransfer) { - var types = null; - var type = ""; - var count = aDataTransfer.mozItemCount; - var len = 0; - for (var i = 0; i < count; ++i) { - this.logger.log(3, "Inspecting the data transfer: " + i); - types = aDataTransfer.mozTypesAt(i); - len = types.length; - for (var j = 0; j < len; ++j) { - type = types[j]; - this.logger.log(3, "Type is: " + type); - if (type == "text/x-moz-url" || - type == "text/x-moz-url-data" || - type == "text/uri-list" || - type == "application/x-moz-file-promise-url") { - aDataTransfer.clearData(type); - this.logger.log(3, "Removing " + type); - } - } + /* + * The _showPrompt() implementation uses some XUL and JS that is part of the + * browser's confirmEx() implementation. Specifically, _showPrompt() depends + * on chrome://global/content/commonDialog.xul as well as some of the code + * in resource://gre/modules/SharedPromptUtils.jsm. + */ + _showPrompt: function(aWindowContext) { + let parentWin; + try { + parentWin = aWindowContext.getInterface(Ci.nsIDOMWindow); + } catch (e) { + parentWin = Services.wm.getMostRecentWindow("navigator:browser"); } - }
+ let title = parentWin.torbutton_get_property_string("torbutton.popup.external.title"); + let app = parentWin.torbutton_get_property_string("torbutton.popup.external.app"); + let note = parentWin.torbutton_get_property_string("torbutton.popup.external.note"); + let suggest = parentWin.torbutton_get_property_string("torbutton.popup.external.suggest"); + let launch = parentWin.torbutton_get_property_string("torbutton.popup.launch"); + let cancel = parentWin.torbutton_get_property_string("torbutton.popup.cancel"); + let dontask = parentWin.torbutton_get_property_string("torbutton.popup.dontask"); + + let args = { + promptType: "confirmEx", + title: title, + text: app+note+suggest+" ", + checkLabel: dontask, + checked: false, + ok: false, + button0Label: launch, + button1Label: cancel, + defaultButtonNum: 1, // Cancel + buttonNumClicked: 1, // Cancel + enableDelay: true, + }; + + let propBag = PromptUtils.objectToPropBag(args); + let uri = "chrome://global/content/commonDialog.xul"; + let promptWin = Services.ww.openWindow(parentWin, uri, "_blank", + "centerscreen,chrome,titlebar", propBag); + promptWin.addEventListener("load", aEvent => { + promptWin.addEventListener("unload", aEvent => { + PromptUtils.propBagToObject(propBag, args); + + if (0 == args.buttonNumClicked) { + // Save the checkbox value and tell the browser's external helper app + // module about the user's choice. + if (args.checked) { + Services.prefs.setBoolPref("extensions.torbutton.launch_warning", + false); + } + + this._helperAppLauncher.continueRequest(); + } else { + this._helperAppLauncher.cancelRequest(Cr.NS_BINDING_ABORTED); + } + }, false); + }, false); + }, };
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ExternalAppBlocker]);