Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | // This test ensures that the nsIUrlClassifierHashCompleter works as expected |
michael@0 | 5 | // and simulates an HTTP server to provide completions. |
michael@0 | 6 | // |
michael@0 | 7 | // In order to test completions, each group of completions sent as one request |
michael@0 | 8 | // to the HTTP server is called a completion set. There is currently not |
michael@0 | 9 | // support for multiple requests being sent to the server at once, in this test. |
michael@0 | 10 | // This tests makes a request for each element of |completionSets|, waits for |
michael@0 | 11 | // a response and then moves to the next element. |
michael@0 | 12 | // Each element of |completionSets| is an array of completions, and each |
michael@0 | 13 | // completion is an object with the properties: |
michael@0 | 14 | // hash: complete hash for the completion. Automatically right-padded |
michael@0 | 15 | // to be COMPLETE_LENGTH. |
michael@0 | 16 | // expectCompletion: boolean indicating whether the server should respond |
michael@0 | 17 | // with a full hash. |
michael@0 | 18 | // table: name of the table that the hash corresponds to. Only needs to be set |
michael@0 | 19 | // if a completion is expected. |
michael@0 | 20 | // chunkId: positive integer corresponding to the chunk that the hash belongs |
michael@0 | 21 | // to. Only needs to be set if a completion is expected. |
michael@0 | 22 | // multipleCompletions: boolean indicating whether the server should respond |
michael@0 | 23 | // with more than one full hash. If this is set to true |
michael@0 | 24 | // then |expectCompletion| must also be set to true and |
michael@0 | 25 | // |hash| must have the same prefix as all |completions|. |
michael@0 | 26 | // completions: an array of completions (objects with a hash, table and |
michael@0 | 27 | // chunkId property as described above). This property is only |
michael@0 | 28 | // used when |multipleCompletions| is set to true. |
michael@0 | 29 | |
michael@0 | 30 | // Basic prefixes with 2/3 completions. |
michael@0 | 31 | let basicCompletionSet = [ |
michael@0 | 32 | { |
michael@0 | 33 | hash: "abcdefgh", |
michael@0 | 34 | expectCompletion: true, |
michael@0 | 35 | table: "test", |
michael@0 | 36 | chunkId: 1234, |
michael@0 | 37 | }, |
michael@0 | 38 | { |
michael@0 | 39 | hash: "1234", |
michael@0 | 40 | expectCompletion: false, |
michael@0 | 41 | }, |
michael@0 | 42 | { |
michael@0 | 43 | hash: "\u0000\u0000\u000012312", |
michael@0 | 44 | expectCompletion: true, |
michael@0 | 45 | table: "test", |
michael@0 | 46 | chunkId: 1234, |
michael@0 | 47 | } |
michael@0 | 48 | ]; |
michael@0 | 49 | |
michael@0 | 50 | // 3 prefixes with 0 completions to test HashCompleter handling a 204 status. |
michael@0 | 51 | let falseCompletionSet = [ |
michael@0 | 52 | { |
michael@0 | 53 | hash: "1234", |
michael@0 | 54 | expectCompletion: false, |
michael@0 | 55 | }, |
michael@0 | 56 | { |
michael@0 | 57 | hash: "", |
michael@0 | 58 | expectCompletion: false, |
michael@0 | 59 | }, |
michael@0 | 60 | { |
michael@0 | 61 | hash: "abc", |
michael@0 | 62 | expectCompletion: false, |
michael@0 | 63 | } |
michael@0 | 64 | ]; |
michael@0 | 65 | |
michael@0 | 66 | // The current implementation (as of Mar 2011) sometimes sends duplicate |
michael@0 | 67 | // entries to HashCompleter and even expects responses for duplicated entries. |
michael@0 | 68 | let dupedCompletionSet = [ |
michael@0 | 69 | { |
michael@0 | 70 | hash: "1234", |
michael@0 | 71 | expectCompletion: true, |
michael@0 | 72 | table: "test", |
michael@0 | 73 | chunkId: 1, |
michael@0 | 74 | }, |
michael@0 | 75 | { |
michael@0 | 76 | hash: "5678", |
michael@0 | 77 | expectCompletion: false, |
michael@0 | 78 | table: "test2", |
michael@0 | 79 | chunkId: 2, |
michael@0 | 80 | }, |
michael@0 | 81 | { |
michael@0 | 82 | hash: "1234", |
michael@0 | 83 | expectCompletion: true, |
michael@0 | 84 | table: "test", |
michael@0 | 85 | chunkId: 1, |
michael@0 | 86 | }, |
michael@0 | 87 | { |
michael@0 | 88 | hash: "5678", |
michael@0 | 89 | expectCompletion: false, |
michael@0 | 90 | table: "test2", |
michael@0 | 91 | chunkId: 2 |
michael@0 | 92 | } |
michael@0 | 93 | ]; |
michael@0 | 94 | |
michael@0 | 95 | // It is possible for a hash completion request to return with multiple |
michael@0 | 96 | // completions, the HashCompleter should return all of these. |
michael@0 | 97 | let multipleResponsesCompletionSet = [ |
michael@0 | 98 | { |
michael@0 | 99 | hash: "1234", |
michael@0 | 100 | expectCompletion: true, |
michael@0 | 101 | multipleCompletions: true, |
michael@0 | 102 | completions: [ |
michael@0 | 103 | { |
michael@0 | 104 | hash: "123456", |
michael@0 | 105 | table: "test1", |
michael@0 | 106 | chunkId: 3, |
michael@0 | 107 | }, |
michael@0 | 108 | { |
michael@0 | 109 | hash: "123478", |
michael@0 | 110 | table: "test2", |
michael@0 | 111 | chunkId: 4, |
michael@0 | 112 | } |
michael@0 | 113 | ], |
michael@0 | 114 | } |
michael@0 | 115 | ]; |
michael@0 | 116 | |
michael@0 | 117 | // The fifth completion set is added at runtime by addRandomCompletionSet. |
michael@0 | 118 | // Each completion in the set only has one response and its purpose is to |
michael@0 | 119 | // provide an easy way to test the HashCompleter handling an arbitrarily large |
michael@0 | 120 | // completion set (determined by SIZE_OF_RANDOM_SET). |
michael@0 | 121 | const SIZE_OF_RANDOM_SET = 16; |
michael@0 | 122 | function addRandomCompletionSet() { |
michael@0 | 123 | let completionSet = []; |
michael@0 | 124 | let hashPrefixes = []; |
michael@0 | 125 | |
michael@0 | 126 | let seed = Math.floor(Math.random() * Math.pow(2, 32)); |
michael@0 | 127 | dump("Using seed of " + seed + " for random completion set.\n"); |
michael@0 | 128 | let rand = new LFSRgenerator(seed); |
michael@0 | 129 | |
michael@0 | 130 | for (let i = 0; i < SIZE_OF_RANDOM_SET; i++) { |
michael@0 | 131 | let completion = {}; |
michael@0 | 132 | |
michael@0 | 133 | // Generate a random 256 bit hash. First we get a random number and then |
michael@0 | 134 | // convert it to a string. |
michael@0 | 135 | let hash; |
michael@0 | 136 | let prefix; |
michael@0 | 137 | do { |
michael@0 | 138 | hash = ""; |
michael@0 | 139 | let length = 1 + rand.nextNum(5); |
michael@0 | 140 | for (let i = 0; i < length; i++) |
michael@0 | 141 | hash += String.fromCharCode(rand.nextNum(8)); |
michael@0 | 142 | prefix = hash.substring(0,4); |
michael@0 | 143 | } while (hashPrefixes.indexOf(prefix) != -1); |
michael@0 | 144 | |
michael@0 | 145 | hashPrefixes.push(prefix); |
michael@0 | 146 | completion.hash = hash; |
michael@0 | 147 | |
michael@0 | 148 | completion.expectCompletion = rand.nextNum(1) == 1; |
michael@0 | 149 | if (completion.expectCompletion) { |
michael@0 | 150 | // Generate a random alpha-numeric string of length at most 6 for the |
michael@0 | 151 | // table name. |
michael@0 | 152 | completion.table = (rand.nextNum(31)).toString(36); |
michael@0 | 153 | |
michael@0 | 154 | completion.chunkId = rand.nextNum(16); |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | completionSet.push(completion); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | completionSets.push(completionSet); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | let completionSets = [basicCompletionSet, falseCompletionSet, |
michael@0 | 164 | dupedCompletionSet, multipleResponsesCompletionSet]; |
michael@0 | 165 | let currentCompletionSet = -1; |
michael@0 | 166 | let finishedCompletions = 0; |
michael@0 | 167 | |
michael@0 | 168 | const SERVER_PORT = 8080; |
michael@0 | 169 | const SERVER_PATH = "/hash-completer"; |
michael@0 | 170 | let server; |
michael@0 | 171 | |
michael@0 | 172 | // Completion hashes are automatically right-padded with null chars to have a |
michael@0 | 173 | // length of COMPLETE_LENGTH. |
michael@0 | 174 | // Taken from nsUrlClassifierDBService.h |
michael@0 | 175 | const COMPLETE_LENGTH = 32; |
michael@0 | 176 | |
michael@0 | 177 | let completer = Cc["@mozilla.org/url-classifier/hashcompleter;1"]. |
michael@0 | 178 | getService(Ci.nsIUrlClassifierHashCompleter); |
michael@0 | 179 | |
michael@0 | 180 | function run_test() { |
michael@0 | 181 | addRandomCompletionSet(); |
michael@0 | 182 | |
michael@0 | 183 | // Fix up the completions before running the test. |
michael@0 | 184 | for each (let completionSet in completionSets) { |
michael@0 | 185 | for each (let completion in completionSet) { |
michael@0 | 186 | // Pad the right of each |hash| so that the length is COMPLETE_LENGTH. |
michael@0 | 187 | if (completion.multipleCompletions) { |
michael@0 | 188 | for each (let responseCompletion in completion.completions) { |
michael@0 | 189 | let numChars = COMPLETE_LENGTH - responseCompletion.hash.length; |
michael@0 | 190 | responseCompletion.hash += (new Array(numChars + 1)).join("\u0000"); |
michael@0 | 191 | } |
michael@0 | 192 | } |
michael@0 | 193 | else { |
michael@0 | 194 | let numChars = COMPLETE_LENGTH - completion.hash.length; |
michael@0 | 195 | completion.hash += (new Array(numChars + 1)).join("\u0000"); |
michael@0 | 196 | } |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | do_test_pending(); |
michael@0 | 200 | |
michael@0 | 201 | server = new HttpServer(); |
michael@0 | 202 | server.registerPathHandler(SERVER_PATH, hashCompleterServer); |
michael@0 | 203 | |
michael@0 | 204 | const SERVER_PORT = 8080; |
michael@0 | 205 | server.start(SERVER_PORT); |
michael@0 | 206 | |
michael@0 | 207 | completer.gethashUrl = "http://localhost:" + SERVER_PORT + SERVER_PATH; |
michael@0 | 208 | |
michael@0 | 209 | runNextCompletion(); |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | function doneCompletionSet() { |
michael@0 | 213 | do_check_eq(finishedCompletions, completionSets[currentCompletionSet].length); |
michael@0 | 214 | |
michael@0 | 215 | for each (let completion in completionSets[currentCompletionSet]) |
michael@0 | 216 | do_check_true(completion._finished); |
michael@0 | 217 | |
michael@0 | 218 | runNextCompletion(); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | function runNextCompletion() { |
michael@0 | 222 | currentCompletionSet++; |
michael@0 | 223 | finishedCompletions = 0; |
michael@0 | 224 | |
michael@0 | 225 | if (currentCompletionSet >= completionSets.length) { |
michael@0 | 226 | finish(); |
michael@0 | 227 | return; |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | dump("Now on completion set index " + currentCompletionSet + "\n"); |
michael@0 | 231 | for each (let completion in completionSets[currentCompletionSet]) { |
michael@0 | 232 | completer.complete(completion.hash.substring(0,4), |
michael@0 | 233 | (new callback(completion))); |
michael@0 | 234 | } |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | function hashCompleterServer(aRequest, aResponse) { |
michael@0 | 238 | let stream = aRequest.bodyInputStream; |
michael@0 | 239 | let wrapperStream = Cc["@mozilla.org/binaryinputstream;1"]. |
michael@0 | 240 | createInstance(Ci.nsIBinaryInputStream); |
michael@0 | 241 | wrapperStream.setInputStream(stream); |
michael@0 | 242 | |
michael@0 | 243 | let len = stream.available(); |
michael@0 | 244 | let data = wrapperStream.readBytes(len); |
michael@0 | 245 | |
michael@0 | 246 | // To avoid a response with duplicate hash completions, we keep track of all |
michael@0 | 247 | // completed hash prefixes so far. |
michael@0 | 248 | let completedHashes = []; |
michael@0 | 249 | let responseText = ""; |
michael@0 | 250 | |
michael@0 | 251 | function responseForCompletion(x) { |
michael@0 | 252 | return x.table + ":" + x.chunkId + ":" + x.hash.length + "\n" + x.hash; |
michael@0 | 253 | } |
michael@0 | 254 | for each (let completion in completionSets[currentCompletionSet]) { |
michael@0 | 255 | if (completion.expectCompletion && |
michael@0 | 256 | (completedHashes.indexOf(completion.hash) == -1)) { |
michael@0 | 257 | completedHashes.push(completion.hash); |
michael@0 | 258 | |
michael@0 | 259 | if (completion.multipleCompletions) |
michael@0 | 260 | responseText += completion.completions.map(responseForCompletion).join(""); |
michael@0 | 261 | else |
michael@0 | 262 | responseText += responseForCompletion(completion); |
michael@0 | 263 | } |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | // As per the spec, a server should response with a 204 if there are no |
michael@0 | 267 | // full-length hashes that match the prefixes. |
michael@0 | 268 | if (responseText) |
michael@0 | 269 | aResponse.write(responseText); |
michael@0 | 270 | else |
michael@0 | 271 | aResponse.setStatusLine(null, 204, null); |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | |
michael@0 | 275 | function callback(completion) { |
michael@0 | 276 | this._completion = completion; |
michael@0 | 277 | } |
michael@0 | 278 | callback.prototype = { |
michael@0 | 279 | completion: function completion(hash, table, chunkId, trusted) { |
michael@0 | 280 | do_check_true(this._completion.expectCompletion); |
michael@0 | 281 | |
michael@0 | 282 | if (this._completion.multipleCompletions) { |
michael@0 | 283 | for each (let completion in this._completion.completions) { |
michael@0 | 284 | if (completion.hash == hash) { |
michael@0 | 285 | do_check_eq(JSON.stringify(hash), JSON.stringify(completion.hash)); |
michael@0 | 286 | do_check_eq(table, completion.table); |
michael@0 | 287 | do_check_eq(chunkId, completion.chunkId); |
michael@0 | 288 | |
michael@0 | 289 | completion._completed = true; |
michael@0 | 290 | |
michael@0 | 291 | if (this._completion.completions.every(function(x) x._completed)) |
michael@0 | 292 | this._completed = true; |
michael@0 | 293 | |
michael@0 | 294 | break; |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | } |
michael@0 | 298 | else { |
michael@0 | 299 | // Hashes are not actually strings and can contain arbitrary data. |
michael@0 | 300 | do_check_eq(JSON.stringify(hash), JSON.stringify(this._completion.hash)); |
michael@0 | 301 | do_check_eq(table, this._completion.table); |
michael@0 | 302 | do_check_eq(chunkId, this._completion.chunkId); |
michael@0 | 303 | |
michael@0 | 304 | this._completed = true; |
michael@0 | 305 | } |
michael@0 | 306 | }, |
michael@0 | 307 | |
michael@0 | 308 | completionFinished: function completionFinished(status) { |
michael@0 | 309 | do_check_eq(!!this._completion.expectCompletion, !!this._completed); |
michael@0 | 310 | this._completion._finished = true; |
michael@0 | 311 | |
michael@0 | 312 | finishedCompletions++; |
michael@0 | 313 | if (finishedCompletions == completionSets[currentCompletionSet].length) |
michael@0 | 314 | doneCompletionSet(); |
michael@0 | 315 | }, |
michael@0 | 316 | }; |
michael@0 | 317 | |
michael@0 | 318 | function finish() { |
michael@0 | 319 | server.stop(function() { |
michael@0 | 320 | do_test_finished(); |
michael@0 | 321 | }); |
michael@0 | 322 | } |