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 | "use strict"; |
michael@0 | 5 | |
michael@0 | 6 | module.metadata = { |
michael@0 | 7 | "stability": "unstable" |
michael@0 | 8 | }; |
michael@0 | 9 | |
michael@0 | 10 | let { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core"); |
michael@0 | 11 | |
michael@0 | 12 | // This module provides set of high order function for working with event |
michael@0 | 13 | // streams (streams in a NodeJS style that dispatch data, end and error |
michael@0 | 14 | // events). |
michael@0 | 15 | |
michael@0 | 16 | // Function takes a `target` object and returns set of implicit references |
michael@0 | 17 | // (non property references) it keeps. This basically allows defining |
michael@0 | 18 | // references between objects without storing the explicitly. See transform for |
michael@0 | 19 | // more details. |
michael@0 | 20 | let refs = (function() { |
michael@0 | 21 | let refSets = new WeakMap(); |
michael@0 | 22 | return function refs(target) { |
michael@0 | 23 | if (!refSets.has(target)) refSets.set(target, new Set()); |
michael@0 | 24 | return refSets.get(target); |
michael@0 | 25 | }; |
michael@0 | 26 | })(); |
michael@0 | 27 | |
michael@0 | 28 | function transform(input, f) { |
michael@0 | 29 | let output = {}; |
michael@0 | 30 | |
michael@0 | 31 | // Since event listeners don't prevent `input` to be GC-ed we wanna presrve |
michael@0 | 32 | // it until `output` can be GC-ed. There for we add implicit reference which |
michael@0 | 33 | // is removed once `input` ends. |
michael@0 | 34 | refs(output).add(input); |
michael@0 | 35 | |
michael@0 | 36 | const next = data => receive(output, data); |
michael@0 | 37 | once(output, "start", () => start(input)); |
michael@0 | 38 | on(input, "error", error => emit(output, "error", error)); |
michael@0 | 39 | on(input, "end", function() { |
michael@0 | 40 | refs(output).delete(input); |
michael@0 | 41 | end(output); |
michael@0 | 42 | }); |
michael@0 | 43 | on(input, "data", data => f(data, next)); |
michael@0 | 44 | return output; |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | // High order event transformation function that takes `input` event channel |
michael@0 | 48 | // and returns transformation containing only events on which `p` predicate |
michael@0 | 49 | // returns `true`. |
michael@0 | 50 | function filter(input, predicate) { |
michael@0 | 51 | return transform(input, function(data, next) { |
michael@0 | 52 | if (predicate(data)) |
michael@0 | 53 | next(data); |
michael@0 | 54 | }); |
michael@0 | 55 | } |
michael@0 | 56 | exports.filter = filter; |
michael@0 | 57 | |
michael@0 | 58 | // High order function that takes `input` and returns input of it's values |
michael@0 | 59 | // mapped via given `f` function. |
michael@0 | 60 | const map = (input, f) => transform(input, (data, next) => next(f(data))); |
michael@0 | 61 | exports.map = map; |
michael@0 | 62 | |
michael@0 | 63 | // High order function that takes `input` stream of streams and merges them |
michael@0 | 64 | // into single event stream. Like flatten but time based rather than order |
michael@0 | 65 | // based. |
michael@0 | 66 | function merge(inputs) { |
michael@0 | 67 | let output = {}; |
michael@0 | 68 | let open = 1; |
michael@0 | 69 | let state = []; |
michael@0 | 70 | output.state = state; |
michael@0 | 71 | refs(output).add(inputs); |
michael@0 | 72 | |
michael@0 | 73 | function end(input) { |
michael@0 | 74 | open = open - 1; |
michael@0 | 75 | refs(output).delete(input); |
michael@0 | 76 | if (open === 0) emit(output, "end"); |
michael@0 | 77 | } |
michael@0 | 78 | const error = e => emit(output, "error", e); |
michael@0 | 79 | function forward(input) { |
michael@0 | 80 | state.push(input); |
michael@0 | 81 | open = open + 1; |
michael@0 | 82 | on(input, "end", () => end(input)); |
michael@0 | 83 | on(input, "error", error); |
michael@0 | 84 | on(input, "data", data => emit(output, "data", data)); |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | // If `inputs` is an array treat it as a stream. |
michael@0 | 88 | if (Array.isArray(inputs)) { |
michael@0 | 89 | inputs.forEach(forward); |
michael@0 | 90 | end(inputs); |
michael@0 | 91 | } |
michael@0 | 92 | else { |
michael@0 | 93 | on(inputs, "end", () => end(inputs)); |
michael@0 | 94 | on(inputs, "error", error); |
michael@0 | 95 | on(inputs, "data", forward); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | return output; |
michael@0 | 99 | } |
michael@0 | 100 | exports.merge = merge; |
michael@0 | 101 | |
michael@0 | 102 | const expand = (inputs, f) => merge(map(inputs, f)); |
michael@0 | 103 | exports.expand = expand; |
michael@0 | 104 | |
michael@0 | 105 | const pipe = (from, to) => on(from, "*", emit.bind(emit, to)); |
michael@0 | 106 | exports.pipe = pipe; |
michael@0 | 107 | |
michael@0 | 108 | |
michael@0 | 109 | // Shim signal APIs so other modules can be used as is. |
michael@0 | 110 | |
michael@0 | 111 | const receive = (input, message) => { |
michael@0 | 112 | if (input[receive]) |
michael@0 | 113 | input[receive](input, message); |
michael@0 | 114 | else |
michael@0 | 115 | emit(input, "data", message); |
michael@0 | 116 | |
michael@0 | 117 | input.value = message; |
michael@0 | 118 | }; |
michael@0 | 119 | receive.toString = () => "@@receive"; |
michael@0 | 120 | exports.receive = receive; |
michael@0 | 121 | exports.send = receive; |
michael@0 | 122 | |
michael@0 | 123 | const end = input => { |
michael@0 | 124 | if (input[end]) |
michael@0 | 125 | input[end](input); |
michael@0 | 126 | else |
michael@0 | 127 | emit(input, "end", input); |
michael@0 | 128 | }; |
michael@0 | 129 | end.toString = () => "@@end"; |
michael@0 | 130 | exports.end = end; |
michael@0 | 131 | |
michael@0 | 132 | const stop = input => { |
michael@0 | 133 | if (input[stop]) |
michael@0 | 134 | input[stop](input); |
michael@0 | 135 | else |
michael@0 | 136 | emit(input, "stop", input); |
michael@0 | 137 | }; |
michael@0 | 138 | stop.toString = () => "@@stop"; |
michael@0 | 139 | exports.stop = stop; |
michael@0 | 140 | |
michael@0 | 141 | const start = input => { |
michael@0 | 142 | if (input[start]) |
michael@0 | 143 | input[start](input); |
michael@0 | 144 | else |
michael@0 | 145 | emit(input, "start", input); |
michael@0 | 146 | }; |
michael@0 | 147 | start.toString = () => "@@start"; |
michael@0 | 148 | exports.start = start; |
michael@0 | 149 | |
michael@0 | 150 | const lift = (step, ...inputs) => { |
michael@0 | 151 | let args = null; |
michael@0 | 152 | let opened = inputs.length; |
michael@0 | 153 | let started = false; |
michael@0 | 154 | const output = {}; |
michael@0 | 155 | const init = () => { |
michael@0 | 156 | args = [...inputs.map(input => input.value)]; |
michael@0 | 157 | output.value = step(...args); |
michael@0 | 158 | }; |
michael@0 | 159 | |
michael@0 | 160 | inputs.forEach((input, index) => { |
michael@0 | 161 | on(input, "data", data => { |
michael@0 | 162 | args[index] = data; |
michael@0 | 163 | receive(output, step(...args)); |
michael@0 | 164 | }); |
michael@0 | 165 | on(input, "end", () => { |
michael@0 | 166 | opened = opened - 1; |
michael@0 | 167 | if (opened <= 0) |
michael@0 | 168 | end(output); |
michael@0 | 169 | }); |
michael@0 | 170 | }); |
michael@0 | 171 | |
michael@0 | 172 | once(output, "start", () => { |
michael@0 | 173 | inputs.forEach(start); |
michael@0 | 174 | init(); |
michael@0 | 175 | }); |
michael@0 | 176 | |
michael@0 | 177 | init(); |
michael@0 | 178 | |
michael@0 | 179 | return output; |
michael@0 | 180 | }; |
michael@0 | 181 | exports.lift = lift; |
michael@0 | 182 | |
michael@0 | 183 | const merges = inputs => { |
michael@0 | 184 | let opened = inputs.length; |
michael@0 | 185 | let output = { value: inputs[0].value }; |
michael@0 | 186 | inputs.forEach((input, index) => { |
michael@0 | 187 | on(input, "data", data => receive(output, data)); |
michael@0 | 188 | on(input, "end", () => { |
michael@0 | 189 | opened = opened - 1; |
michael@0 | 190 | if (opened <= 0) |
michael@0 | 191 | end(output); |
michael@0 | 192 | }); |
michael@0 | 193 | }); |
michael@0 | 194 | |
michael@0 | 195 | once(output, "start", () => { |
michael@0 | 196 | inputs.forEach(start); |
michael@0 | 197 | output.value = inputs[0].value; |
michael@0 | 198 | }); |
michael@0 | 199 | |
michael@0 | 200 | return output; |
michael@0 | 201 | }; |
michael@0 | 202 | exports.merges = merges; |
michael@0 | 203 | |
michael@0 | 204 | const foldp = (step, initial, input) => { |
michael@0 | 205 | let output = map(input, x => step(output.value, x)); |
michael@0 | 206 | output.value = initial; |
michael@0 | 207 | return output; |
michael@0 | 208 | }; |
michael@0 | 209 | exports.foldp = foldp; |
michael@0 | 210 | |
michael@0 | 211 | const keepIf = (p, base, input) => { |
michael@0 | 212 | let output = filter(input, p); |
michael@0 | 213 | output.value = base; |
michael@0 | 214 | return output; |
michael@0 | 215 | }; |
michael@0 | 216 | exports.keepIf = keepIf; |
michael@0 | 217 | |
michael@0 | 218 | function Input() {} |
michael@0 | 219 | Input.start = input => emit(input, "start", input); |
michael@0 | 220 | Input.prototype.start = Input.start; |
michael@0 | 221 | |
michael@0 | 222 | Input.end = input => { |
michael@0 | 223 | emit(input, "end", input); |
michael@0 | 224 | stop(input); |
michael@0 | 225 | }; |
michael@0 | 226 | Input.prototype[end] = Input.end; |
michael@0 | 227 | |
michael@0 | 228 | exports.Input = Input; |
michael@0 | 229 | |
michael@0 | 230 | const $source = "@@source"; |
michael@0 | 231 | const $outputs = "@@outputs"; |
michael@0 | 232 | exports.outputs = $outputs; |
michael@0 | 233 | |
michael@0 | 234 | function Reactor(options={}) { |
michael@0 | 235 | const {onStep, onStart, onEnd} = options; |
michael@0 | 236 | if (onStep) |
michael@0 | 237 | this.onStep = onStep; |
michael@0 | 238 | if (onStart) |
michael@0 | 239 | this.onStart = onStart; |
michael@0 | 240 | if (onEnd) |
michael@0 | 241 | this.onEnd = onEnd; |
michael@0 | 242 | } |
michael@0 | 243 | Reactor.prototype.onStep = _ => void(0); |
michael@0 | 244 | Reactor.prototype.onStart = _ => void(0); |
michael@0 | 245 | Reactor.prototype.onEnd = _ => void(0); |
michael@0 | 246 | Reactor.prototype.onNext = function(present, past) { |
michael@0 | 247 | this.value = present; |
michael@0 | 248 | this.onStep(present, past); |
michael@0 | 249 | }; |
michael@0 | 250 | Reactor.prototype.run = function(input) { |
michael@0 | 251 | on(input, "data", message => this.onNext(message, input.value)); |
michael@0 | 252 | on(input, "end", () => this.onEnd(input.value)); |
michael@0 | 253 | start(input); |
michael@0 | 254 | this.value = input.value; |
michael@0 | 255 | this.onStart(input.value); |
michael@0 | 256 | }; |
michael@0 | 257 | exports.Reactor = Reactor; |
michael@0 | 258 | |
michael@0 | 259 | /** |
michael@0 | 260 | * Takes an object used as options with potential keys like 'onMessage', |
michael@0 | 261 | * used to be called `require('sdk/event/core').setListeners` on. |
michael@0 | 262 | * This strips all keys that would trigger a listener to be set. |
michael@0 | 263 | * |
michael@0 | 264 | * @params {Object} object |
michael@0 | 265 | * @return {Object} |
michael@0 | 266 | */ |
michael@0 | 267 | |
michael@0 | 268 | function stripListeners (object) { |
michael@0 | 269 | return Object.keys(object || {}).reduce((agg, key) => { |
michael@0 | 270 | if (!EVENT_TYPE_PATTERN.test(key)) |
michael@0 | 271 | agg[key] = object[key]; |
michael@0 | 272 | return agg; |
michael@0 | 273 | }, {}); |
michael@0 | 274 | } |
michael@0 | 275 | exports.stripListeners = stripListeners; |