Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | Cu.import("resource://testing-common/httpd.js"); |
michael@0 | 6 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 7 | |
michael@0 | 8 | var httpserver; |
michael@0 | 9 | |
michael@0 | 10 | function inChildProcess() { |
michael@0 | 11 | return Cc["@mozilla.org/xre/app-info;1"] |
michael@0 | 12 | .getService(Ci.nsIXULRuntime) |
michael@0 | 13 | .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; |
michael@0 | 14 | } |
michael@0 | 15 | function makeChan(path) { |
michael@0 | 16 | var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); |
michael@0 | 17 | var chan = ios.newChannel("http://localhost:" + httpserver.identity.primaryPort + "/" + path, null, null) |
michael@0 | 18 | .QueryInterface(Ci.nsIHttpChannel); |
michael@0 | 19 | return chan; |
michael@0 | 20 | } |
michael@0 | 21 | |
michael@0 | 22 | function setup_chan(path, isPrivate, callback) { |
michael@0 | 23 | var chan = makeChan(path); |
michael@0 | 24 | chan.QueryInterface(Ci.nsIPrivateBrowsingChannel).setPrivate(isPrivate); |
michael@0 | 25 | chan.asyncOpen(new ChannelListener(callback), null); |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | function set_cookie(value, callback) { |
michael@0 | 29 | return setup_chan('set?cookie=' + value, false, callback); |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | function set_private_cookie(value, callback) { |
michael@0 | 33 | return setup_chan('set?cookie=' + value, true, callback); |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | function check_cookie_presence(value, isPrivate, expected, callback) { |
michael@0 | 37 | var chan = setup_chan('present?cookie=' + value.replace('=','|'), isPrivate, function(req) { |
michael@0 | 38 | req.QueryInterface(Ci.nsIHttpChannel); |
michael@0 | 39 | do_check_eq(req.responseStatus, expected ? 200 : 404); |
michael@0 | 40 | callback(req); |
michael@0 | 41 | }); |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | function presentHandler(metadata, response) { |
michael@0 | 45 | var present = false; |
michael@0 | 46 | var match = /cookie=([^&]*)/.exec(metadata.queryString); |
michael@0 | 47 | if (match) { |
michael@0 | 48 | try { |
michael@0 | 49 | present = metadata.getHeader("Cookie").indexOf(match[1].replace("|","=")) != -1; |
michael@0 | 50 | } catch (x) { |
michael@0 | 51 | } |
michael@0 | 52 | } |
michael@0 | 53 | response.setStatusLine("1.0", present ? 200 : 404, ""); |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | function setHandler(metadata, response) { |
michael@0 | 57 | response.setStatusLine("1.0", 200, "Cookie set"); |
michael@0 | 58 | var match = /cookie=([^&]*)/.exec(metadata.queryString); |
michael@0 | 59 | if (match) { |
michael@0 | 60 | response.setHeader("Set-Cookie", match[1]); |
michael@0 | 61 | } |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | function run_test() { |
michael@0 | 65 | // Allow all cookies if the pref service is available in this process. |
michael@0 | 66 | if (!inChildProcess()) |
michael@0 | 67 | Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); |
michael@0 | 68 | |
michael@0 | 69 | httpserver = new HttpServer(); |
michael@0 | 70 | httpserver.registerPathHandler("/set", setHandler); |
michael@0 | 71 | httpserver.registerPathHandler("/present", presentHandler); |
michael@0 | 72 | httpserver.start(-1); |
michael@0 | 73 | |
michael@0 | 74 | do_test_pending(); |
michael@0 | 75 | |
michael@0 | 76 | function check_cookie(req) { |
michael@0 | 77 | req.QueryInterface(Ci.nsIHttpChannel); |
michael@0 | 78 | do_check_eq(req.responseStatus, 200); |
michael@0 | 79 | try { |
michael@0 | 80 | do_check_true(req.getResponseHeader("Set-Cookie") != "", "expected a Set-Cookie header"); |
michael@0 | 81 | } catch (x) { |
michael@0 | 82 | do_throw("missing Set-Cookie header"); |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | runNextTest(); |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | let tests = []; |
michael@0 | 89 | |
michael@0 | 90 | function runNextTest() { |
michael@0 | 91 | do_execute_soon(tests.shift()); |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | tests.push(function() { |
michael@0 | 95 | set_cookie("C1=V1", check_cookie); |
michael@0 | 96 | }); |
michael@0 | 97 | tests.push(function() { |
michael@0 | 98 | set_private_cookie("C2=V2", check_cookie); |
michael@0 | 99 | }); |
michael@0 | 100 | tests.push(function() { |
michael@0 | 101 | // Check that the first cookie is present in a non-private request |
michael@0 | 102 | check_cookie_presence("C1=V1", false, true, runNextTest); |
michael@0 | 103 | }); |
michael@0 | 104 | tests.push(function() { |
michael@0 | 105 | // Check that the second cookie is present in a private request |
michael@0 | 106 | check_cookie_presence("C2=V2", true, true, runNextTest); |
michael@0 | 107 | }); |
michael@0 | 108 | tests.push(function() { |
michael@0 | 109 | // Check that the first cookie is not present in a private request |
michael@0 | 110 | check_cookie_presence("C1=V1", true, false, runNextTest); |
michael@0 | 111 | }); |
michael@0 | 112 | tests.push(function() { |
michael@0 | 113 | // Check that the second cookie is not present in a non-private request |
michael@0 | 114 | check_cookie_presence("C2=V2", false, false, runNextTest); |
michael@0 | 115 | }); |
michael@0 | 116 | |
michael@0 | 117 | // The following test only works in a non-e10s situation at the moment, |
michael@0 | 118 | // since the notification needs to run in the parent process but there is |
michael@0 | 119 | // no existing mechanism to make that happen. |
michael@0 | 120 | if (!inChildProcess()) { |
michael@0 | 121 | tests.push(function() { |
michael@0 | 122 | // Simulate all private browsing instances being closed |
michael@0 | 123 | var obsvc = Cc["@mozilla.org/observer-service;1"]. |
michael@0 | 124 | getService(Ci.nsIObserverService); |
michael@0 | 125 | obsvc.notifyObservers(null, "last-pb-context-exited", null); |
michael@0 | 126 | // Check that all private cookies are now unavailable in new private requests |
michael@0 | 127 | check_cookie_presence("C2=V2", true, false, runNextTest); |
michael@0 | 128 | }); |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | tests.push(function() { httpserver.stop(do_test_finished); }); |
michael@0 | 132 | |
michael@0 | 133 | runNextTest(); |
michael@0 | 134 | } |