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 | module.metadata = { |
michael@0 | 7 | "stability": "deprecated" |
michael@0 | 8 | }; |
michael@0 | 9 | |
michael@0 | 10 | const memory = require("./memory"); |
michael@0 | 11 | |
michael@0 | 12 | const { merge } = require("../util/object"); |
michael@0 | 13 | const { union } = require("../util/array"); |
michael@0 | 14 | const { isNil, isRegExp } = require("../lang/type"); |
michael@0 | 15 | |
michael@0 | 16 | // The possible return values of getTypeOf. |
michael@0 | 17 | const VALID_TYPES = [ |
michael@0 | 18 | "array", |
michael@0 | 19 | "boolean", |
michael@0 | 20 | "function", |
michael@0 | 21 | "null", |
michael@0 | 22 | "number", |
michael@0 | 23 | "object", |
michael@0 | 24 | "string", |
michael@0 | 25 | "undefined", |
michael@0 | 26 | "regexp" |
michael@0 | 27 | ]; |
michael@0 | 28 | |
michael@0 | 29 | const { isArray } = Array; |
michael@0 | 30 | |
michael@0 | 31 | /** |
michael@0 | 32 | * Returns a validated options dictionary given some requirements. If any of |
michael@0 | 33 | * the requirements are not met, an exception is thrown. |
michael@0 | 34 | * |
michael@0 | 35 | * @param options |
michael@0 | 36 | * An object, the options dictionary to validate. It's not modified. |
michael@0 | 37 | * If it's null or otherwise falsey, an empty object is assumed. |
michael@0 | 38 | * @param requirements |
michael@0 | 39 | * An object whose keys are the expected keys in options. Any key in |
michael@0 | 40 | * options that is not present in requirements is ignored. Each value |
michael@0 | 41 | * in requirements is itself an object describing the requirements of |
michael@0 | 42 | * its key. There are four optional keys in this object: |
michael@0 | 43 | * map: A function that's passed the value of the key in options. |
michael@0 | 44 | * map's return value is taken as the key's value in the final |
michael@0 | 45 | * validated options, is, and ok. If map throws an exception |
michael@0 | 46 | * it's caught and discarded, and the key's value is its value in |
michael@0 | 47 | * options. |
michael@0 | 48 | * is: An array containing any number of the typeof type names. If |
michael@0 | 49 | * the key's value is none of these types, it fails validation. |
michael@0 | 50 | * Arrays, null and regexps are identified by the special type names |
michael@0 | 51 | * "array", "null", "regexp"; "object" will not match either. No type |
michael@0 | 52 | * coercion is done. |
michael@0 | 53 | * ok: A function that's passed the key's value. If it returns |
michael@0 | 54 | * false, the value fails validation. |
michael@0 | 55 | * msg: If the key's value fails validation, an exception is thrown. |
michael@0 | 56 | * This string will be used as its message. If undefined, a |
michael@0 | 57 | * generic message is used, unless is is defined, in which case |
michael@0 | 58 | * the message will state that the value needs to be one of the |
michael@0 | 59 | * given types. |
michael@0 | 60 | * @return An object whose keys are those keys in requirements that are also in |
michael@0 | 61 | * options and whose values are the corresponding return values of map |
michael@0 | 62 | * or the corresponding values in options. Note that any keys not |
michael@0 | 63 | * shared by both requirements and options are not in the returned |
michael@0 | 64 | * object. |
michael@0 | 65 | */ |
michael@0 | 66 | exports.validateOptions = function validateOptions(options, requirements) { |
michael@0 | 67 | options = options || {}; |
michael@0 | 68 | let validatedOptions = {}; |
michael@0 | 69 | |
michael@0 | 70 | for (let key in requirements) { |
michael@0 | 71 | let isOptional = false; |
michael@0 | 72 | let mapThrew = false; |
michael@0 | 73 | let req = requirements[key]; |
michael@0 | 74 | let [optsVal, keyInOpts] = (key in options) ? |
michael@0 | 75 | [options[key], true] : |
michael@0 | 76 | [undefined, false]; |
michael@0 | 77 | if (req.map) { |
michael@0 | 78 | try { |
michael@0 | 79 | optsVal = req.map(optsVal); |
michael@0 | 80 | } |
michael@0 | 81 | catch (err) { |
michael@0 | 82 | if (err instanceof RequirementError) |
michael@0 | 83 | throw err; |
michael@0 | 84 | |
michael@0 | 85 | mapThrew = true; |
michael@0 | 86 | } |
michael@0 | 87 | } |
michael@0 | 88 | if (req.is) { |
michael@0 | 89 | let types = req.is; |
michael@0 | 90 | |
michael@0 | 91 | if (!isArray(types) && isArray(types.is)) |
michael@0 | 92 | types = types.is; |
michael@0 | 93 | |
michael@0 | 94 | if (isArray(types)) { |
michael@0 | 95 | isOptional = ['undefined', 'null'].every(v => ~types.indexOf(v)); |
michael@0 | 96 | |
michael@0 | 97 | // Sanity check the caller's type names. |
michael@0 | 98 | types.forEach(function (typ) { |
michael@0 | 99 | if (VALID_TYPES.indexOf(typ) < 0) { |
michael@0 | 100 | let msg = 'Internal error: invalid requirement type "' + typ + '".'; |
michael@0 | 101 | throw new Error(msg); |
michael@0 | 102 | } |
michael@0 | 103 | }); |
michael@0 | 104 | if (types.indexOf(getTypeOf(optsVal)) < 0) |
michael@0 | 105 | throw new RequirementError(key, req); |
michael@0 | 106 | } |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | if (req.ok && ((!isOptional || !isNil(optsVal)) && !req.ok(optsVal))) |
michael@0 | 110 | throw new RequirementError(key, req); |
michael@0 | 111 | |
michael@0 | 112 | if (keyInOpts || (req.map && !mapThrew && optsVal !== undefined)) |
michael@0 | 113 | validatedOptions[key] = optsVal; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | return validatedOptions; |
michael@0 | 117 | }; |
michael@0 | 118 | |
michael@0 | 119 | exports.addIterator = function addIterator(obj, keysValsGenerator) { |
michael@0 | 120 | obj.__iterator__ = function(keysOnly, keysVals) { |
michael@0 | 121 | let keysValsIterator = keysValsGenerator.call(this); |
michael@0 | 122 | |
michael@0 | 123 | // "for (.. in ..)" gets only keys, "for each (.. in ..)" gets values, |
michael@0 | 124 | // and "for (.. in Iterator(..))" gets [key, value] pairs. |
michael@0 | 125 | let index = keysOnly ? 0 : 1; |
michael@0 | 126 | while (true) |
michael@0 | 127 | yield keysVals ? keysValsIterator.next() : keysValsIterator.next()[index]; |
michael@0 | 128 | }; |
michael@0 | 129 | }; |
michael@0 | 130 | |
michael@0 | 131 | // Similar to typeof, except arrays, null and regexps are identified by "array" and |
michael@0 | 132 | // "null" and "regexp", not "object". |
michael@0 | 133 | let getTypeOf = exports.getTypeOf = function getTypeOf(val) { |
michael@0 | 134 | let typ = typeof(val); |
michael@0 | 135 | if (typ === "object") { |
michael@0 | 136 | if (!val) |
michael@0 | 137 | return "null"; |
michael@0 | 138 | if (isArray(val)) |
michael@0 | 139 | return "array"; |
michael@0 | 140 | if (isRegExp(val)) |
michael@0 | 141 | return "regexp"; |
michael@0 | 142 | } |
michael@0 | 143 | return typ; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | function RequirementError(key, requirement) { |
michael@0 | 147 | Error.call(this); |
michael@0 | 148 | |
michael@0 | 149 | this.name = "RequirementError"; |
michael@0 | 150 | |
michael@0 | 151 | let msg = requirement.msg; |
michael@0 | 152 | if (!msg) { |
michael@0 | 153 | msg = 'The option "' + key + '" '; |
michael@0 | 154 | msg += requirement.is ? |
michael@0 | 155 | "must be one of the following types: " + requirement.is.join(", ") : |
michael@0 | 156 | "is invalid."; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | this.message = msg; |
michael@0 | 160 | } |
michael@0 | 161 | RequirementError.prototype = Object.create(Error.prototype); |
michael@0 | 162 | |
michael@0 | 163 | let string = { is: ['string', 'undefined', 'null'] }; |
michael@0 | 164 | exports.string = string; |
michael@0 | 165 | |
michael@0 | 166 | let number = { is: ['number', 'undefined', 'null'] }; |
michael@0 | 167 | exports.number = number; |
michael@0 | 168 | |
michael@0 | 169 | let boolean = { is: ['boolean', 'undefined', 'null'] }; |
michael@0 | 170 | exports.boolean = boolean; |
michael@0 | 171 | |
michael@0 | 172 | let object = { is: ['object', 'undefined', 'null'] }; |
michael@0 | 173 | exports.object = object; |
michael@0 | 174 | |
michael@0 | 175 | let isTruthyType = type => !(type === 'undefined' || type === 'null'); |
michael@0 | 176 | let findTypes = v => { while (!isArray(v) && v.is) v = v.is; return v }; |
michael@0 | 177 | |
michael@0 | 178 | function required(req) { |
michael@0 | 179 | let types = (findTypes(req) || VALID_TYPES).filter(isTruthyType); |
michael@0 | 180 | |
michael@0 | 181 | return merge({}, req, {is: types}); |
michael@0 | 182 | } |
michael@0 | 183 | exports.required = required; |
michael@0 | 184 | |
michael@0 | 185 | function optional(req) { |
michael@0 | 186 | req = merge({is: []}, req); |
michael@0 | 187 | req.is = findTypes(req).filter(isTruthyType).concat('undefined', 'null'); |
michael@0 | 188 | |
michael@0 | 189 | return req; |
michael@0 | 190 | } |
michael@0 | 191 | exports.optional = optional; |
michael@0 | 192 | |
michael@0 | 193 | function either(...types) { |
michael@0 | 194 | return union.apply(null, types.map(findTypes)); |
michael@0 | 195 | } |
michael@0 | 196 | exports.either = either; |