diff --git a/bootstrap.js b/bootstrap.js index b27f75d..bb485ac 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -40,6 +40,8 @@ var windowsObserver = { _dbg = prefs.get("debug", false); _dbgv = prefs.get("debug.verbose", false); + this.initPrivateProtocol(); + this.patchPrivateBrowsingUtils(true); this.initHotkeys(); this.appButtonDontChange = !prefs.get("fixAppButtonWidth"); @@ -55,6 +57,8 @@ var windowsObserver = { return; this.initialized = false; + this.destroyPrivateProtocol(); + if(reason == ADDON_DISABLE || reason == ADDON_UNINSTALL) this.askToClosePrivateTabs(); @@ -165,6 +169,21 @@ var windowsObserver = { window.removeEventListener("beforeunload", this, false); }, + initPrivateProtocol: function() { + if("privateProtocol" in this) + return; + var tmp = {}; + Services.scriptloader.loadSubScript("chrome://privatetab/content/protocol.js", tmp, "UTF-8"); + var privateProtocol = this.privateProtocol = tmp.privateProtocol; + privateProtocol.init(); + }, + destroyPrivateProtocol: function() { + if("privateProtocol" in this) { + this.privateProtocol.destroy(); + delete this.privateProtocol; + } + }, + initWindow: function(window, reason) { if(reason == WINDOW_LOADED && !this.isTargetWindow(window)) { if(this.isViewSourceWindow(window)) diff --git a/make.bat b/make.bat index c207515..cd8fc8e 100644 --- a/make.bat +++ b/make.bat @@ -19,7 +19,7 @@ if not exist %_7zip% ( cd /d "%~dp0" -set _files=install.rdf *.manifest *.js *.jsm *.xul *.xml license* changelog* *.png defaults modules components locale chrome idl +set _files=install.rdf *.manifest *.js *.jsm *.xul *.xml *.html license* changelog* *.png defaults modules components locale chrome idl if exist %_7zip% ( echo =^> %_7zip% %_7zip% a -tzip -mx9 -mfb=258 -mpass=15 -- %_out_tmp% %_files% diff --git a/protocol.js b/protocol.js new file mode 100644 index 0000000..a2a72f3 --- /dev/null +++ b/protocol.js @@ -0,0 +1,130 @@ +const P_CID = Components.ID("{e974cf10-11cb-4293-af88-e61c7dfe717c}"), + P_CONTRACTID = "@mozilla.org/network/protocol;1?name=private", + P_HANDLER = Components.interfaces.nsIProtocolHandler, + P_SCHEME = "private", + P_NAME = "Private Tab protocol handler"; + +var privateProtocol = { + get compReg() { + return Components.manager + .QueryInterface(Components.interfaces.nsIComponentRegistrar); + }, + init: function() { + this.compReg.registerFactory(P_CID, P_NAME, P_CONTRACTID, this); + _log("[protocol] Initialized"); + }, + destroy: function() { + this.compReg.unregisterFactory(P_CID, this); + _log("[protocol] Destroyed"); + }, + + // nsIFactory + createInstance: function(outer, iid) { + if(outer != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + if(iid.equals(P_HANDLER)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + lockFactory: function(lock) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, + // nsISupports + QueryInterface: function(iid) { + if( + iid.equals(Components.interfaces.nsISupports) + || iid.equals(Components.interfaces.nsIFactory) + || iid.equals(P_HANDLER) + ) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + // nsIProtocolHandler + defaultPort: -1, + protocolFlags: P_HANDLER.URI_NORELATIVE + | P_HANDLER.URI_NOAUTH + | P_HANDLER.URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT + //| P_HANDLER.URI_LOADABLE_BY_ANYONE + | P_HANDLER.URI_DANGEROUS_TO_LOAD + | P_HANDLER.URI_NON_PERSISTABLE, + scheme: P_SCHEME, + allowPort: function() { + return false; + }, + newURI: function(spec, originCharset, baseURI) { + var url = Components.classes["@mozilla.org/network/standard-url;1"] + .createInstance(Components.interfaces.nsIStandardURL); + url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD, 0, spec, originCharset, baseURI); + return url.QueryInterface(Components.interfaces.nsIURI); + }, + newChannel: function(uri) { + var spec = uri.spec; + _log("[protocol] newChannel(): spec = " + spec); + if(!spec || !spec.startsWith(P_SCHEME + ":")) + return null; + var newSpec = spec.replace(/^private:\/*#?/i, ""); + _log("[protocol] newChannel(): newSpec = " + newSpec); + try { + Services.io.newURI(newSpec, null, null); + } + catch(e) { + _log("[protocol] newChannel(): malformed URI"); + Components.utils.reportError(e); + return null; + } + + // We can't use newChannel(newSpec, ...) here - strange things happens + // Also we can't use nsIPrivateBrowsingChannel.setPrivate(true) for chrome:// URI + var redirect = "chrome://privatetab/content/protocolRedirect.html#" + newSpec; + var channel = Services.io.newChannel(redirect, null, null); + var channelWrapper = { + __proto__: channel, + _makePrivate: this.makeChannelPrivate.bind(this, channel), + asyncOpen: function(aListener, aContext) { + _log("[protocol] nsIChannel.asyncOpen()"); + this._makePrivate(); + return channel.asyncOpen.apply(this, arguments); + }, + open: function() { + _log("[protocol] nsIChannel.open()"); + this._makePrivate(); + return channel.open.apply(this, arguments); + } + }; + return channelWrapper; + }, + + makeChannelPrivate: function(channel) { + try { + if(channel.notificationCallbacks) { + channel.notificationCallbacks + .getInterface(Components.interfaces.nsILoadContext) + .usePrivateBrowsing = true; + return; + } + } + catch(e) { + Components.utils.reportError(e); + } + try { + if(channel.loadGroup && channel.loadGroup.notificationCallbacks) { + channel.loadGroup.notificationCallbacks + .getInterface(Components.interfaces.nsILoadContext) + .usePrivateBrowsing = true; + return; + } + } + catch(e) { + Components.utils.reportError(e); + } + if(channel instanceof Components.interfaces.nsIPrivateBrowsingChannel) try { + channel.setPrivate(true); + return; + } + catch(e) { + Components.utils.reportError(e); + } + throw Components.results.NS_ERROR_NO_INTERFACE; + } +}; \ No newline at end of file diff --git a/protocolRedirect.html b/protocolRedirect.html new file mode 100644 index 0000000..a48502e --- /dev/null +++ b/protocolRedirect.html @@ -0,0 +1,78 @@ + + +Private Tab: Redirect… + \ No newline at end of file