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 | /* 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 | 'use strict'; |
michael@0 | 6 | |
michael@0 | 7 | module.metadata = { |
michael@0 | 8 | 'stability': 'experimental' |
michael@0 | 9 | }; |
michael@0 | 10 | |
michael@0 | 11 | /* |
michael@0 | 12 | * Encodings supported by TextEncoder/Decoder: |
michael@0 | 13 | * utf-8, utf-16le, utf-16be |
michael@0 | 14 | * http://encoding.spec.whatwg.org/#interface-textencoder |
michael@0 | 15 | * |
michael@0 | 16 | * Node however supports the following encodings: |
michael@0 | 17 | * ascii, utf-8, utf-16le, usc2, base64, hex |
michael@0 | 18 | */ |
michael@0 | 19 | |
michael@0 | 20 | const { Cu } = require('chrome'); |
michael@0 | 21 | const { isNumber } = require('sdk/lang/type'); |
michael@0 | 22 | const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {}); |
michael@0 | 23 | |
michael@0 | 24 | exports.TextEncoder = TextEncoder; |
michael@0 | 25 | exports.TextDecoder = TextDecoder; |
michael@0 | 26 | |
michael@0 | 27 | /** |
michael@0 | 28 | * Use WeakMaps to work around Bug 929146, which prevents us from adding |
michael@0 | 29 | * getters or values to typed arrays |
michael@0 | 30 | * https://bugzilla.mozilla.org/show_bug.cgi?id=929146 |
michael@0 | 31 | */ |
michael@0 | 32 | const parents = new WeakMap(); |
michael@0 | 33 | const views = new WeakMap(); |
michael@0 | 34 | |
michael@0 | 35 | function Buffer(subject, encoding /*, bufferLength */) { |
michael@0 | 36 | |
michael@0 | 37 | // Allow invocation without `new` constructor |
michael@0 | 38 | if (!(this instanceof Buffer)) |
michael@0 | 39 | return new Buffer(subject, encoding, arguments[2]); |
michael@0 | 40 | |
michael@0 | 41 | var type = typeof(subject); |
michael@0 | 42 | |
michael@0 | 43 | switch (type) { |
michael@0 | 44 | case 'number': |
michael@0 | 45 | // Create typed array of the given size if number. |
michael@0 | 46 | try { |
michael@0 | 47 | let buffer = new Uint8Array(subject > 0 ? Math.floor(subject) : 0); |
michael@0 | 48 | return buffer; |
michael@0 | 49 | } catch (e) { |
michael@0 | 50 | if (/size and count too large/.test(e.message) || |
michael@0 | 51 | /invalid arguments/.test(e.message)) |
michael@0 | 52 | throw new RangeError('Could not instantiate buffer: size of buffer may be too large'); |
michael@0 | 53 | else |
michael@0 | 54 | throw new Error('Could not instantiate buffer'); |
michael@0 | 55 | } |
michael@0 | 56 | break; |
michael@0 | 57 | case 'string': |
michael@0 | 58 | // If string encode it and use buffer for the returned Uint8Array |
michael@0 | 59 | // to create a local patched version that acts like node buffer. |
michael@0 | 60 | encoding = encoding || 'utf8'; |
michael@0 | 61 | return new Uint8Array(new TextEncoder(encoding).encode(subject).buffer); |
michael@0 | 62 | case 'object': |
michael@0 | 63 | // This form of the constructor uses the form of |
michael@0 | 64 | // new Uint8Array(buffer, offset, length); |
michael@0 | 65 | // So we can instantiate a typed array within the constructor |
michael@0 | 66 | // to inherit the appropriate properties, where both the |
michael@0 | 67 | // `subject` and newly instantiated buffer share the same underlying |
michael@0 | 68 | // data structure. |
michael@0 | 69 | if (arguments.length === 3) |
michael@0 | 70 | return new Uint8Array(subject, encoding, arguments[2]); |
michael@0 | 71 | // If array or alike just make a copy with a local patched prototype. |
michael@0 | 72 | else |
michael@0 | 73 | return new Uint8Array(subject); |
michael@0 | 74 | default: |
michael@0 | 75 | throw new TypeError('must start with number, buffer, array or string'); |
michael@0 | 76 | } |
michael@0 | 77 | } |
michael@0 | 78 | exports.Buffer = Buffer; |
michael@0 | 79 | |
michael@0 | 80 | // Tests if `value` is a Buffer. |
michael@0 | 81 | Buffer.isBuffer = value => value instanceof Buffer |
michael@0 | 82 | |
michael@0 | 83 | // Returns true if the encoding is a valid encoding argument & false otherwise |
michael@0 | 84 | Buffer.isEncoding = function (encoding) { |
michael@0 | 85 | if (!encoding) return false; |
michael@0 | 86 | try { |
michael@0 | 87 | new TextDecoder(encoding); |
michael@0 | 88 | } catch(e) { |
michael@0 | 89 | return false; |
michael@0 | 90 | } |
michael@0 | 91 | return true; |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | // Gives the actual byte length of a string. encoding defaults to 'utf8'. |
michael@0 | 95 | // This is not the same as String.prototype.length since that returns the |
michael@0 | 96 | // number of characters in a string. |
michael@0 | 97 | Buffer.byteLength = (value, encoding = 'utf8') => |
michael@0 | 98 | new TextEncoder(encoding).encode(value).byteLength |
michael@0 | 99 | |
michael@0 | 100 | // Direct copy of the nodejs's buffer implementation: |
michael@0 | 101 | // https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177 |
michael@0 | 102 | Buffer.concat = function(list, length) { |
michael@0 | 103 | if (!Array.isArray(list)) |
michael@0 | 104 | throw new TypeError('Usage: Buffer.concat(list[, length])'); |
michael@0 | 105 | |
michael@0 | 106 | if (typeof length === 'undefined') { |
michael@0 | 107 | length = 0; |
michael@0 | 108 | for (var i = 0; i < list.length; i++) |
michael@0 | 109 | length += list[i].length; |
michael@0 | 110 | } else { |
michael@0 | 111 | length = ~~length; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | if (length < 0) |
michael@0 | 115 | length = 0; |
michael@0 | 116 | |
michael@0 | 117 | if (list.length === 0) |
michael@0 | 118 | return new Buffer(0); |
michael@0 | 119 | else if (list.length === 1) |
michael@0 | 120 | return list[0]; |
michael@0 | 121 | |
michael@0 | 122 | if (length < 0) |
michael@0 | 123 | throw new RangeError('length is not a positive number'); |
michael@0 | 124 | |
michael@0 | 125 | var buffer = new Buffer(length); |
michael@0 | 126 | var pos = 0; |
michael@0 | 127 | for (var i = 0; i < list.length; i++) { |
michael@0 | 128 | var buf = list[i]; |
michael@0 | 129 | buf.copy(buffer, pos); |
michael@0 | 130 | pos += buf.length; |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | return buffer; |
michael@0 | 134 | }; |
michael@0 | 135 | |
michael@0 | 136 | // Node buffer is very much like Uint8Array although it has bunch of methods |
michael@0 | 137 | // that typically can be used in combination with `DataView` while preserving |
michael@0 | 138 | // access by index. Since in SDK each module has it's own set of bult-ins it |
michael@0 | 139 | // ok to patch ours to make it nodejs Buffer compatible. |
michael@0 | 140 | Buffer.prototype = Uint8Array.prototype; |
michael@0 | 141 | Object.defineProperties(Buffer.prototype, { |
michael@0 | 142 | parent: { |
michael@0 | 143 | get: function() { return parents.get(this, undefined); } |
michael@0 | 144 | }, |
michael@0 | 145 | view: { |
michael@0 | 146 | get: function () { |
michael@0 | 147 | let view = views.get(this, undefined); |
michael@0 | 148 | if (view) return view; |
michael@0 | 149 | view = new DataView(this.buffer); |
michael@0 | 150 | views.set(this, view); |
michael@0 | 151 | return view; |
michael@0 | 152 | } |
michael@0 | 153 | }, |
michael@0 | 154 | toString: { |
michael@0 | 155 | value: function(encoding, start, end) { |
michael@0 | 156 | encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8'; |
michael@0 | 157 | start = Math.max(0, ~~start); |
michael@0 | 158 | end = Math.min(this.length, end === void(0) ? this.length : ~~end); |
michael@0 | 159 | return new TextDecoder(encoding).decode(this.subarray(start, end)); |
michael@0 | 160 | } |
michael@0 | 161 | }, |
michael@0 | 162 | toJSON: { |
michael@0 | 163 | value: function() { |
michael@0 | 164 | return { type: 'Buffer', data: Array.slice(this, 0) }; |
michael@0 | 165 | } |
michael@0 | 166 | }, |
michael@0 | 167 | get: { |
michael@0 | 168 | value: function(offset) { |
michael@0 | 169 | return this[offset]; |
michael@0 | 170 | } |
michael@0 | 171 | }, |
michael@0 | 172 | set: { |
michael@0 | 173 | value: function(offset, value) { this[offset] = value; } |
michael@0 | 174 | }, |
michael@0 | 175 | copy: { |
michael@0 | 176 | value: function(target, offset, start, end) { |
michael@0 | 177 | let length = this.length; |
michael@0 | 178 | let targetLength = target.length; |
michael@0 | 179 | offset = isNumber(offset) ? offset : 0; |
michael@0 | 180 | start = isNumber(start) ? start : 0; |
michael@0 | 181 | |
michael@0 | 182 | if (start < 0) |
michael@0 | 183 | throw new RangeError('sourceStart is outside of valid range'); |
michael@0 | 184 | if (end < 0) |
michael@0 | 185 | throw new RangeError('sourceEnd is outside of valid range'); |
michael@0 | 186 | |
michael@0 | 187 | // If sourceStart > sourceEnd, or targetStart > targetLength, |
michael@0 | 188 | // zero bytes copied |
michael@0 | 189 | if (start > end || |
michael@0 | 190 | offset > targetLength |
michael@0 | 191 | ) |
michael@0 | 192 | return 0; |
michael@0 | 193 | |
michael@0 | 194 | // If `end` is not defined, or if it is defined |
michael@0 | 195 | // but would overflow `target`, redefine `end` |
michael@0 | 196 | // so we can copy as much as we can |
michael@0 | 197 | if (end - start > targetLength - offset || |
michael@0 | 198 | end == null) { |
michael@0 | 199 | let remainingTarget = targetLength - offset; |
michael@0 | 200 | let remainingSource = length - start; |
michael@0 | 201 | if (remainingSource <= remainingTarget) |
michael@0 | 202 | end = length; |
michael@0 | 203 | else |
michael@0 | 204 | end = start + remainingTarget; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | Uint8Array.set(target, this.subarray(start, end), offset); |
michael@0 | 208 | return end - start; |
michael@0 | 209 | } |
michael@0 | 210 | }, |
michael@0 | 211 | slice: { |
michael@0 | 212 | value: function(start, end) { |
michael@0 | 213 | let length = this.length; |
michael@0 | 214 | start = ~~start; |
michael@0 | 215 | end = end != null ? end : length; |
michael@0 | 216 | |
michael@0 | 217 | if (start < 0) { |
michael@0 | 218 | start += length; |
michael@0 | 219 | if (start < 0) start = 0; |
michael@0 | 220 | } else if (start > length) |
michael@0 | 221 | start = length; |
michael@0 | 222 | |
michael@0 | 223 | if (end < 0) { |
michael@0 | 224 | end += length; |
michael@0 | 225 | if (end < 0) end = 0; |
michael@0 | 226 | } else if (end > length) |
michael@0 | 227 | end = length; |
michael@0 | 228 | |
michael@0 | 229 | if (end < start) |
michael@0 | 230 | end = start; |
michael@0 | 231 | |
michael@0 | 232 | // This instantiation uses the new Uint8Array(buffer, offset, length) version |
michael@0 | 233 | // of construction to share the same underling data structure |
michael@0 | 234 | let buffer = new Buffer(this.buffer, start, end - start); |
michael@0 | 235 | |
michael@0 | 236 | // If buffer has a value, assign its parent value to the |
michael@0 | 237 | // buffer it shares its underlying structure with. If a slice of |
michael@0 | 238 | // a slice, then use the root structure |
michael@0 | 239 | if (buffer.length > 0) |
michael@0 | 240 | parents.set(buffer, this.parent || this); |
michael@0 | 241 | |
michael@0 | 242 | return buffer; |
michael@0 | 243 | } |
michael@0 | 244 | }, |
michael@0 | 245 | write: { |
michael@0 | 246 | value: function(string, offset, length, encoding = 'utf8') { |
michael@0 | 247 | // write(string, encoding); |
michael@0 | 248 | if (typeof(offset) === 'string' && Number.isNaN(parseInt(offset))) { |
michael@0 | 249 | ([offset, length, encoding]) = [0, null, offset]; |
michael@0 | 250 | } |
michael@0 | 251 | // write(string, offset, encoding); |
michael@0 | 252 | else if (typeof(length) === 'string') |
michael@0 | 253 | ([length, encoding]) = [null, length]; |
michael@0 | 254 | |
michael@0 | 255 | if (offset < 0 || offset > this.length) |
michael@0 | 256 | throw new RangeError('offset is outside of valid range'); |
michael@0 | 257 | |
michael@0 | 258 | offset = ~~offset; |
michael@0 | 259 | |
michael@0 | 260 | // Clamp length if it would overflow buffer, or if its |
michael@0 | 261 | // undefined |
michael@0 | 262 | if (length == null || length + offset > this.length) |
michael@0 | 263 | length = this.length - offset; |
michael@0 | 264 | |
michael@0 | 265 | let buffer = new TextEncoder(encoding).encode(string); |
michael@0 | 266 | let result = Math.min(buffer.length, length); |
michael@0 | 267 | if (buffer.length !== length) |
michael@0 | 268 | buffer = buffer.subarray(0, length); |
michael@0 | 269 | |
michael@0 | 270 | Uint8Array.set(this, buffer, offset); |
michael@0 | 271 | return result; |
michael@0 | 272 | } |
michael@0 | 273 | }, |
michael@0 | 274 | fill: { |
michael@0 | 275 | value: function fill(value, start, end) { |
michael@0 | 276 | let length = this.length; |
michael@0 | 277 | value = value || 0; |
michael@0 | 278 | start = start || 0; |
michael@0 | 279 | end = end || length; |
michael@0 | 280 | |
michael@0 | 281 | if (typeof(value) === 'string') |
michael@0 | 282 | value = value.charCodeAt(0); |
michael@0 | 283 | if (typeof(value) !== 'number' || isNaN(value)) |
michael@0 | 284 | throw TypeError('value is not a number'); |
michael@0 | 285 | if (end < start) |
michael@0 | 286 | throw new RangeError('end < start'); |
michael@0 | 287 | |
michael@0 | 288 | // Fill 0 bytes; we're done |
michael@0 | 289 | if (end === start) |
michael@0 | 290 | return 0; |
michael@0 | 291 | if (length == 0) |
michael@0 | 292 | return 0; |
michael@0 | 293 | |
michael@0 | 294 | if (start < 0 || start >= length) |
michael@0 | 295 | throw RangeError('start out of bounds'); |
michael@0 | 296 | |
michael@0 | 297 | if (end < 0 || end > length) |
michael@0 | 298 | throw RangeError('end out of bounds'); |
michael@0 | 299 | |
michael@0 | 300 | let index = start; |
michael@0 | 301 | while (index < end) this[index++] = value; |
michael@0 | 302 | } |
michael@0 | 303 | } |
michael@0 | 304 | }); |
michael@0 | 305 | |
michael@0 | 306 | // Define nodejs Buffer's getter and setter functions that just proxy |
michael@0 | 307 | // to internal DataView's equivalent methods. |
michael@0 | 308 | |
michael@0 | 309 | // TODO do we need to check architecture to see if it's default big/little endian? |
michael@0 | 310 | [['readUInt16LE', 'getUint16', true], |
michael@0 | 311 | ['readUInt16BE', 'getUint16', false], |
michael@0 | 312 | ['readInt16LE', 'getInt16', true], |
michael@0 | 313 | ['readInt16BE', 'getInt16', false], |
michael@0 | 314 | ['readUInt32LE', 'getUint32', true], |
michael@0 | 315 | ['readUInt32BE', 'getUint32', false], |
michael@0 | 316 | ['readInt32LE', 'getInt32', true], |
michael@0 | 317 | ['readInt32BE', 'getInt32', false], |
michael@0 | 318 | ['readFloatLE', 'getFloat32', true], |
michael@0 | 319 | ['readFloatBE', 'getFloat32', false], |
michael@0 | 320 | ['readDoubleLE', 'getFloat64', true], |
michael@0 | 321 | ['readDoubleBE', 'getFloat64', false], |
michael@0 | 322 | ['readUInt8', 'getUint8'], |
michael@0 | 323 | ['readInt8', 'getInt8']].forEach(([alias, name, littleEndian]) => { |
michael@0 | 324 | Object.defineProperty(Buffer.prototype, alias, { |
michael@0 | 325 | value: function(offset) this.view[name](offset, littleEndian) |
michael@0 | 326 | }); |
michael@0 | 327 | }); |
michael@0 | 328 | |
michael@0 | 329 | [['writeUInt16LE', 'setUint16', true], |
michael@0 | 330 | ['writeUInt16BE', 'setUint16', false], |
michael@0 | 331 | ['writeInt16LE', 'setInt16', true], |
michael@0 | 332 | ['writeInt16BE', 'setInt16', false], |
michael@0 | 333 | ['writeUInt32LE', 'setUint32', true], |
michael@0 | 334 | ['writeUInt32BE', 'setUint32', false], |
michael@0 | 335 | ['writeInt32LE', 'setInt32', true], |
michael@0 | 336 | ['writeInt32BE', 'setInt32', false], |
michael@0 | 337 | ['writeFloatLE', 'setFloat32', true], |
michael@0 | 338 | ['writeFloatBE', 'setFloat32', false], |
michael@0 | 339 | ['writeDoubleLE', 'setFloat64', true], |
michael@0 | 340 | ['writeDoubleBE', 'setFloat64', false], |
michael@0 | 341 | ['writeUInt8', 'setUint8'], |
michael@0 | 342 | ['writeInt8', 'setInt8']].forEach(([alias, name, littleEndian]) => { |
michael@0 | 343 | Object.defineProperty(Buffer.prototype, alias, { |
michael@0 | 344 | value: function(value, offset) this.view[name](offset, value, littleEndian) |
michael@0 | 345 | }); |
michael@0 | 346 | }); |