-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
4 changed files
with
228 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |