Skip to content

Commit

Permalink
Implement "private" protocol
Browse files Browse the repository at this point in the history
(draft for #89)
  • Loading branch information
Infocatcher committed Aug 8, 2013
1 parent 96a14f8 commit fe69013
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 1 deletion.
19 changes: 19 additions & 0 deletions bootstrap.js
Expand Up @@ -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");
Expand All @@ -55,6 +57,8 @@ var windowsObserver = {
return;
this.initialized = false;

this.destroyPrivateProtocol();

if(reason == ADDON_DISABLE || reason == ADDON_UNINSTALL)
this.askToClosePrivateTabs();

Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion make.bat
Expand Up @@ -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%
Expand Down
130 changes: 130 additions & 0 deletions 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;
}
};
78 changes: 78 additions & 0 deletions protocolRedirect.html
@@ -0,0 +1,78 @@
<!DOCTYPE HTML>
<meta charset="utf-8" />
<title>Private Tab: Redirect…</title>
<script>
try {
var spec = location.hash.substr(1);
if(!spec)
throw new Error("No URI");
Components.utils.import("resource://gre/modules/Services.jsm");
var uri = Services.io.newURI(spec, null, null); // We accept only valid URIs

try {
var iconURL = uri.scheme + "://" + uri.hostPort + "/favicon.ico";
var icon = document.createElement("link");
icon.rel = "shortcut icon";
icon.href = iconURL;
document.documentElement.insertBefore(icon, document.documentElement.firstChild);
}
catch(e2) {
}

try {
var dwu = Components.classes["@mozilla.org/inspector/dom-utils;1"]
.getService(Components.interfaces.inIDOMUtils);
var browser = dwu.getParentForNode(document, true);
if(
!(browser instanceof Components.interfaces.nsIDOMXULElement)
|| browser.localName.toLowerCase() != "browser"
)
throw new Error("Can't get XUL browser");
var doc = browser.ownerDocument;
var win = doc.defaultView;
var gBrowser = win.gBrowser;

// Link may be loaded in current tab, so we should send notification
var tab;
if("_getTabForBrowser" in gBrowser)
tab = gBrowser._getTabForBrowser(browser);
else { // SeaMonkey
for(var i = 0, tabs = gBrowser.tabs, l = tabs.length; i < l; ++i) {
if(tabs[i].linkedBrowser == browser) {
tab = tabs[i];
break;
}
}
}
if(tab) {
var evt = doc.createEvent("UIEvent");
evt.initUIEvent("PrivateTab:PrivateChanged", true, false, win, 1);
tab.dispatchEvent(evt);
}

//win.setTimeout(function() {
// gBrowser.setTabTitleLoading(tab);
//}, 0);
var getString = function(id) {
try {
return gBrowser.mStringBundle.getString(id);
}
catch(e) {
}
return undefined;
}
document.title = getString("tabs.connecting")
|| getString("tabs.loading") // SeaMonkey
|| spec;
}
catch(e2) {
document.title = spec;
Components.utils.reportError(e2);
}
location.replace(spec);
}
catch(e) {
document.title = "Private Tab: Can't redirect";
throw e;
}
</script>

0 comments on commit fe69013

Please sign in to comment.