[or-cvs] r20116: {} Implement workaround for https://bugzilla.mozilla.org/show_b (torbutton/trunk/src/components)

mikeperry at seul.org mikeperry at seul.org
Thu Jul 23 02:27:55 UTC 2009


Author: mikeperry
Date: 2009-07-22 22:27:54 -0400 (Wed, 22 Jul 2009)
New Revision: 20116

Added:
   torbutton/trunk/src/components/external-app-blocker.js
Log:

Implement workaround for
https://bugzilla.mozilla.org/show_bug.cgi?id=440892: Present
our own custom confirm dialog before launching external apps.



Added: torbutton/trunk/src/components/external-app-blocker.js
===================================================================
--- torbutton/trunk/src/components/external-app-blocker.js	                        (rev 0)
+++ torbutton/trunk/src/components/external-app-blocker.js	2009-07-23 02:27:54 UTC (rev 20116)
@@ -0,0 +1,253 @@
+/*************************************************************************
+ * External App Handler.
+ * Handles displaying confirmation dialogs for external apps and protocols
+ * due to Firefox Bug https://bugzilla.mozilla.org/show_bug.cgi?id=440892
+ *************************************************************************/
+
+// Module specific constants
+const kMODULE_NAME = "Torbutton External App Handler";
+
+// XXX: Also psm-external-content-listener?
+const kMODULE_CONTRACTID_APP = "@mozilla.org/uriloader/external-helper-app-service;1";
+const kMODULE_CONTRACTID_PROTO = "@mozilla.org/uriloader/external-protocol-service;1";
+
+const kMODULE_CID = Components.ID("3da0269f-fc29-4e9e-a678-c3b1cafcf13f");
+
+/* Mozilla defined interfaces for FF3.0 */
+const kREAL_EXTERNAL_CID = "{A7F800E0-4306-11d4-98D0-001083010E9B}";
+
+const kExternalInterfaces = ["nsIObserver", "nsIMIMEService",
+                             "nsIExternalHelperAppService",
+                             "nsISupportsWeakReference", // XXX: Uh-oh...
+                             "nsIExternalProtocolService",
+                             "nsPIExternalAppLauncher"];
+
+const Cr = Components.results;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
+                .getService(Components.interfaces.nsIXULAppInfo);
+var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
+                       .getService(Components.interfaces.nsIVersionComparator);
+var is_ff3 = (versionChecker.compare(appInfo.version, "3.0a1") >= 0);
+
+function ExternalWrapper() {
+  this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
+      .getService(Components.interfaces.nsISupports).wrappedJSObject;
+  this.logger.log(3, "Component Load 0: New ExternalWrapper.");
+
+  this._real_external = Components.classesByID[kREAL_EXTERNAL_CID];
+  this._interfaces = kExternalInterfaces;
+
+  this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefBranch);
+
+  this._external = function() {
+    var external = this._real_external.getService();
+    for (var i = 0; i < this._interfaces.length; i++) {
+      external.QueryInterface(Components.interfaces[this._interfaces[i]]);
+    }
+    return external;
+  };
+    
+  this.copyMethods(this._external());
+}
+
+ExternalWrapper.prototype =
+{
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIClassInfo)
+        || iid.equals(Components.interfaces.nsISupports)) {
+      return this;
+    }
+
+    var external = this._external().QueryInterface(iid);
+    this.copyMethods(external);
+    return this;
+  },
+
+  // make this an nsIClassInfo object
+  flags: Components.interfaces.nsIClassInfo.DOM_OBJECT,
+
+  // method of nsIClassInfo
+
+  classDescription: "@mozilla.org/uriloader/external-helper-app-service;1",
+  contractID: "@mozilla.org/uriloader/external-helper-app-service;1",
+  classID: kMODULE_CID,
+
+  // method of nsIClassInfo
+  getInterfaces: function(count) {
+    var interfaceList = [Components.interfaces.nsIClassInfo];
+    for (var i = 0; i < this._interfaces.length; i++) {
+      interfaceList.push(Components.interfaces[this._interfaces[i]]);
+    }
+
+    count.value = interfaceList.length;
+    return interfaceList;
+  },
+
+  // method of nsIClassInfo  
+  getHelperForLanguage: function(count) { return null; },
+
+  /* Determine whether we should ask the user to run the app */
+  blockApp: function() {
+    return this._prefs.getBoolPref("extensions.torbutton.tor_enabled");
+  },
+
+  /* Copies methods from the true object we are wrapping */
+  copyMethods: function(wrapped) {
+    var mimic = function(newObj, method) {
+       if(method == "XXXX") {
+          // Hack to deal with unimplemented methods.
+          // XXX: the API docs say to RETURN the not implemented error
+          // for these functions as opposed to throw...
+          var fun = "(function (){return Components.results.NS_ERROR_NOT_IMPLEMENTED; })";
+          newObj[method] = eval(fun);
+       } else if(typeof(wrapped[method]) == "function") {
+          // Code courtesy of timeless: 
+          // http://www.webwizardry.net/~timeless/windowStubs.js
+          var params = [];
+          params.length = wrapped[method].length;
+          var x = 0;
+          var call;
+          if(params.length) call = "("+params.join().replace(/(?:)/g,function(){return "p"+(++x)})+")";
+          else call = "()";
+          var fun = "(function "+call+"{if (arguments.length < "+wrapped[method].length+") throw Components.results.NS_ERROR_XPC_NOT_ENOUGH_ARGS; return wrapped."+method+".apply(wrapped, arguments);})";
+          newObj[method] = eval(fun);
+       } else {
+          newObj.__defineGetter__(method, function() { return wrapped[method]; });
+          newObj.__defineSetter__(method, function(val) { wrapped[method] = val; });
+      }
+    };
+    for (var method in wrapped) {
+      if(typeof(this[method]) == "undefined") mimic(this, method);
+    }
+  },
+
+
+  loadURI: function(aUri, aContext) {
+    if(this.blockApp()) {
+      var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+                 .getService(Components.interfaces.nsIWindowMediator);
+      var chrome = wm.getMostRecentWindow("navigator:browser");
+
+      var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+                              .getService(Components.interfaces.nsIPromptService);
+      var check = {value: false};
+      // XXX: Localize
+      var result = prompts.confirmCheck(chrome, "Load external content?",
+                                        "Do you want to launch an external app to handle"
+                                        +"\n"+aUri.spec+
+                                        "\n\nNote: External apps are NOT Tor safe by default and can unmask you!\n", 
+                                        "Do not ask me again", check);
+
+      // do something check.value / result
+      if (check.value) {
+        // XXX: Set a pref...
+      }
+
+      if (!result) {
+        return null;
+      }
+ 
+    }
+ 
+    return this._external().loadURI(aUri, aContext);
+  },
+
+  // loadUrl calls loadURI
+
+  doContent: function(aMimeContentType, aRequest, aWindowContext, aForceSave) {
+    if(this.blockApp()) {
+      var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+                 .getService(Components.interfaces.nsIWindowMediator);
+      var chrome = wm.getMostRecentWindow("navigator:browser");
+
+      var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+                              .getService(Components.interfaces.nsIPromptService);
+      var check = {value: false};
+      // XXX: Localize
+      var result = prompts.confirmCheck(chrome, "Load external content?",
+                                        "Do you want to launch an external app to handle"
+                                        +"\n"+aRequest.name+
+                                        "\n\nNote: External apps are NOT Tor safe by default and can unmask you!\n", 
+                                        "Do not ask me again", check);
+      // do something check.value / result
+      if (check.value) {
+        // XXX: Set a pref...
+      }
+
+      if (!result) {
+        return null;
+      }
+    }
+ 
+    return this._external().doContent(aMimeContentType, aRequest, aWindowContext, aForceSave);
+  },
+
+};
+
+var ExternalWrapperSingleton = null;
+var ExternalWrapperFactory = new Object();
+
+ExternalWrapperFactory.createInstance = function (outer, iid)
+{
+  if (outer != null) {
+    Components.returnCode = Cr.NS_ERROR_NO_AGGREGATION;
+    return null;
+  }
+
+  if(!ExternalWrapperSingleton)
+    ExternalWrapperSingleton = new ExternalWrapper();
+
+  return ExternalWrapperSingleton;
+};
+
+
+/**
+ * JS XPCOM component registration goop:
+ *
+ * Everything below is boring boilerplate and can probably be ignored.
+ */
+
+var ExternalWrapperModule = new Object();
+
+ExternalWrapperModule.registerSelf = 
+function (compMgr, fileSpec, location, type) {
+  var nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
+  compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
+  compMgr.registerFactoryLocation(kMODULE_CID,
+                                  kMODULE_NAME,
+                                  kMODULE_CONTRACTID_APP,
+                                  fileSpec, 
+                                  location, 
+                                  type);
+
+  compMgr.registerFactoryLocation(kMODULE_CID,
+                                  kMODULE_NAME,
+                                  kMODULE_CONTRACTID_PROTO,
+                                  fileSpec, 
+                                  location, 
+                                  type);
+};
+
+ExternalWrapperModule.getClassObject = function (compMgr, cid, iid)
+{
+  if (cid.equals(kMODULE_CID))
+    return ExternalWrapperFactory;
+
+  Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
+  return null;
+};
+
+ExternalWrapperModule.canUnload = function (compMgr)
+{
+  return true;
+};
+
+function NSGetModule(compMgr, fileSpec)
+{
+  return ExternalWrapperModule;
+}
+



More information about the tor-commits mailing list