addon-sdk/source/lib/sdk/deprecated/traits/core.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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": "deprecated"
michael@0 9 };
michael@0 10
michael@0 11 // Design inspired by: http://www.traitsjs.org/
michael@0 12
michael@0 13 // shortcuts
michael@0 14 const getOwnPropertyNames = Object.getOwnPropertyNames,
michael@0 15 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
michael@0 16 hasOwn = Object.prototype.hasOwnProperty,
michael@0 17 _create = Object.create;
michael@0 18
michael@0 19 function doPropertiesMatch(object1, object2, name) {
michael@0 20 // If `object1` has property with the given `name`
michael@0 21 return name in object1 ?
michael@0 22 // then `object2` should have it with the same value.
michael@0 23 name in object2 && object1[name] === object2[name] :
michael@0 24 // otherwise `object2` should not have property with the given `name`.
michael@0 25 !(name in object2);
michael@0 26 }
michael@0 27
michael@0 28 /**
michael@0 29 * Compares two trait custom property descriptors if they are the same. If
michael@0 30 * both are `conflict` or all the properties of descriptor are equal returned
michael@0 31 * value will be `true`, otherwise it will be `false`.
michael@0 32 * @param {Object} desc1
michael@0 33 * @param {Object} desc2
michael@0 34 */
michael@0 35 function areSame(desc1, desc2) {
michael@0 36 return ('conflict' in desc1 && desc1.conflict &&
michael@0 37 'conflict' in desc2 && desc2.conflict) ||
michael@0 38 (doPropertiesMatch(desc1, desc2, 'get') &&
michael@0 39 doPropertiesMatch(desc1, desc2, 'set') &&
michael@0 40 doPropertiesMatch(desc1, desc2, 'value') &&
michael@0 41 doPropertiesMatch(desc1, desc2, 'enumerable') &&
michael@0 42 doPropertiesMatch(desc1, desc2, 'required') &&
michael@0 43 doPropertiesMatch(desc1, desc2, 'conflict'));
michael@0 44 }
michael@0 45
michael@0 46 /**
michael@0 47 * Converts array to an object whose own property names represent
michael@0 48 * values of array.
michael@0 49 * @param {String[]} names
michael@0 50 * @returns {Object}
michael@0 51 * @example
michael@0 52 * Map(['foo', ...]) => { foo: true, ...}
michael@0 53 */
michael@0 54 function Map(names) {
michael@0 55 let map = {};
michael@0 56 for each (let name in names)
michael@0 57 map[name] = true;
michael@0 58 return map;
michael@0 59 }
michael@0 60
michael@0 61
michael@0 62 const ERR_CONFLICT = 'Remaining conflicting property: ',
michael@0 63 ERR_REQUIRED = 'Missing required property: ';
michael@0 64 /**
michael@0 65 * Constant singleton, representing placeholder for required properties.
michael@0 66 * @type {Object}
michael@0 67 */
michael@0 68 const required = { toString: function()'<Trait.required>' };
michael@0 69 exports.required = required;
michael@0 70
michael@0 71 /**
michael@0 72 * Generates custom **required** property descriptor. Descriptor contains
michael@0 73 * non-standard property `required` that is equal to `true`.
michael@0 74 * @param {String} name
michael@0 75 * property name to generate descriptor for.
michael@0 76 * @returns {Object}
michael@0 77 * custom property descriptor
michael@0 78 */
michael@0 79 function Required(name) {
michael@0 80 function required() { throw new Error(ERR_REQUIRED + name) }
michael@0 81 return {
michael@0 82 get: required,
michael@0 83 set: required,
michael@0 84 required: true
michael@0 85 };
michael@0 86 }
michael@0 87
michael@0 88 /**
michael@0 89 * Generates custom **conflicting** property descriptor. Descriptor contains
michael@0 90 * non-standard property `conflict` that is equal to `true`.
michael@0 91 * @param {String} name
michael@0 92 * property name to generate descriptor for.
michael@0 93 * @returns {Object}
michael@0 94 * custom property descriptor
michael@0 95 */
michael@0 96 function Conflict(name) {
michael@0 97 function conflict() { throw new Error(ERR_CONFLICT + name) }
michael@0 98 return {
michael@0 99 get: conflict,
michael@0 100 set: conflict,
michael@0 101 conflict: true
michael@0 102 };
michael@0 103 }
michael@0 104
michael@0 105 /**
michael@0 106 * Function generates custom properties descriptor of the `object`s own
michael@0 107 * properties. All the inherited properties are going to be ignored.
michael@0 108 * Properties with values matching `required` singleton will be marked as
michael@0 109 * 'required' properties.
michael@0 110 * @param {Object} object
michael@0 111 * Set of properties to generate trait from.
michael@0 112 * @returns {Object}
michael@0 113 * Properties descriptor of all of the `object`'s own properties.
michael@0 114 */
michael@0 115 function trait(properties) {
michael@0 116 let result = {},
michael@0 117 keys = getOwnPropertyNames(properties);
michael@0 118 for each (let key in keys) {
michael@0 119 let descriptor = getOwnPropertyDescriptor(properties, key);
michael@0 120 result[key] = (required === descriptor.value) ? Required(key) : descriptor;
michael@0 121 }
michael@0 122 return result;
michael@0 123 }
michael@0 124 exports.Trait = exports.trait = trait;
michael@0 125
michael@0 126 /**
michael@0 127 * Composes new trait. If two or more traits have own properties with the
michael@0 128 * same name, the new trait will contain a 'conflict' property for that name.
michael@0 129 * 'compose' is a commutative and associative operation, and the order of its
michael@0 130 * arguments is not significant.
michael@0 131 *
michael@0 132 * @params {Object} trait
michael@0 133 * Takes traits as an arguments
michael@0 134 * @returns {Object}
michael@0 135 * New trait containing the combined own properties of all the traits.
michael@0 136 * @example
michael@0 137 * var newTrait = compose(trait_1, trait_2, ..., trait_N);
michael@0 138 */
michael@0 139 function compose(trait1, trait2) {
michael@0 140 let traits = Array.slice(arguments, 0),
michael@0 141 result = {};
michael@0 142 for each (let trait in traits) {
michael@0 143 let keys = getOwnPropertyNames(trait);
michael@0 144 for each (let key in keys) {
michael@0 145 let descriptor = trait[key];
michael@0 146 // if property already exists and it's not a requirement
michael@0 147 if (hasOwn.call(result, key) && !result[key].required) {
michael@0 148 if (descriptor.required)
michael@0 149 continue;
michael@0 150 if (!areSame(descriptor, result[key]))
michael@0 151 result[key] = Conflict(key);
michael@0 152 } else {
michael@0 153 result[key] = descriptor;
michael@0 154 }
michael@0 155 }
michael@0 156 }
michael@0 157 return result;
michael@0 158 }
michael@0 159 exports.compose = compose;
michael@0 160
michael@0 161 /**
michael@0 162 * Composes new trait with the same own properties as the original trait,
michael@0 163 * except that all property names appearing in the first argument are replaced
michael@0 164 * by 'required' property descriptors.
michael@0 165 * @param {String[]} keys
michael@0 166 * Array of strings property names.
michael@0 167 * @param {Object} trait
michael@0 168 * A trait some properties of which should be excluded.
michael@0 169 * @returns {Object}
michael@0 170 * @example
michael@0 171 * var newTrait = exclude(['name', ...], trait)
michael@0 172 */
michael@0 173 function exclude(keys, trait) {
michael@0 174 let exclusions = Map(keys),
michael@0 175 result = {};
michael@0 176
michael@0 177 keys = getOwnPropertyNames(trait);
michael@0 178
michael@0 179 for each (let key in keys) {
michael@0 180 if (!hasOwn.call(exclusions, key) || trait[key].required)
michael@0 181 result[key] = trait[key];
michael@0 182 else
michael@0 183 result[key] = Required(key);
michael@0 184 }
michael@0 185 return result;
michael@0 186 }
michael@0 187
michael@0 188 /**
michael@0 189 * Composes a new trait with all of the combined properties of the argument
michael@0 190 * traits. In contrast to `compose`, `override` immediately resolves all
michael@0 191 * conflicts resulting from this composition by overriding the properties of
michael@0 192 * later traits. Trait priority is from left to right. I.e. the properties of
michael@0 193 * the leftmost trait are never overridden.
michael@0 194 * @params {Object} trait
michael@0 195 * @returns {Object}
michael@0 196 * @examples
michael@0 197 * // override is associative:
michael@0 198 * override(t1,t2,t3)
michael@0 199 * // is equivalent to
michael@0 200 * override(t1, override(t2, t3))
michael@0 201 * // or
michael@0 202 * to override(override(t1, t2), t3)
michael@0 203 *
michael@0 204 * // override is not commutative:
michael@0 205 * override(t1,t2)
michael@0 206 * // is not equivalent to
michael@0 207 * override(t2,t1)
michael@0 208 */
michael@0 209 function override() {
michael@0 210 let traits = Array.slice(arguments, 0),
michael@0 211 result = {};
michael@0 212 for each (let trait in traits) {
michael@0 213 let keys = getOwnPropertyNames(trait);
michael@0 214 for each(let key in keys) {
michael@0 215 let descriptor = trait[key];
michael@0 216 if (!hasOwn.call(result, key) || result[key].required)
michael@0 217 result[key] = descriptor;
michael@0 218 }
michael@0 219 }
michael@0 220 return result;
michael@0 221 }
michael@0 222 exports.override = override;
michael@0 223
michael@0 224 /**
michael@0 225 * Composes a new trait with the same properties as the original trait, except
michael@0 226 * that all properties whose name is an own property of map will be renamed to
michael@0 227 * map[name], and a 'required' property for name will be added instead.
michael@0 228 * @param {Object} map
michael@0 229 * An object whose own properties serve as a mapping from old names to new
michael@0 230 * names.
michael@0 231 * @param {Object} trait
michael@0 232 * A trait object
michael@0 233 * @returns {Object}
michael@0 234 * @example
michael@0 235 * var newTrait = rename(map, trait);
michael@0 236 */
michael@0 237 function rename(map, trait) {
michael@0 238 let result = {},
michael@0 239 keys = getOwnPropertyNames(trait);
michael@0 240 for each(let key in keys) {
michael@0 241 // must be renamed & it's not requirement
michael@0 242 if (hasOwn.call(map, key) && !trait[key].required) {
michael@0 243 let alias = map[key];
michael@0 244 if (hasOwn.call(result, alias) && !result[alias].required)
michael@0 245 result[alias] = Conflict(alias);
michael@0 246 else
michael@0 247 result[alias] = trait[key];
michael@0 248 if (!hasOwn.call(result, key))
michael@0 249 result[key] = Required(key);
michael@0 250 } else { // must not be renamed or its a requirement
michael@0 251 // property is not in result trait yet
michael@0 252 if (!hasOwn.call(result, key))
michael@0 253 result[key] = trait[key];
michael@0 254 // property is already in resulted trait & it's not requirement
michael@0 255 else if (!trait[key].required)
michael@0 256 result[key] = Conflict(key);
michael@0 257 }
michael@0 258 }
michael@0 259 return result;
michael@0 260 }
michael@0 261
michael@0 262 /**
michael@0 263 * Composes new resolved trait, with all the same properties as the original
michael@0 264 * trait, except that all properties whose name is an own property of
michael@0 265 * resolutions will be renamed to `resolutions[name]`. If it is
michael@0 266 * `resolutions[name]` is `null` value is changed into a required property
michael@0 267 * descriptor.
michael@0 268 * function can be implemented as `rename(map,exclude(exclusions, trait))`
michael@0 269 * where map is the subset of mappings from oldName to newName and exclusions
michael@0 270 * is an array of all the keys that map to `null`.
michael@0 271 * Note: it's important to **first** `exclude`, **then** `rename`, since
michael@0 272 * `exclude` and rename are not associative.
michael@0 273 * @param {Object} resolutions
michael@0 274 * An object whose own properties serve as a mapping from old names to new
michael@0 275 * names, or to `null` if the property should be excluded.
michael@0 276 * @param {Object} trait
michael@0 277 * A trait object
michael@0 278 * @returns {Object}
michael@0 279 * Resolved trait with the same own properties as the original trait.
michael@0 280 */
michael@0 281 function resolve(resolutions, trait) {
michael@0 282 let renames = {},
michael@0 283 exclusions = [],
michael@0 284 keys = getOwnPropertyNames(resolutions);
michael@0 285 for each (let key in keys) { // pre-process renamed and excluded properties
michael@0 286 if (resolutions[key]) // old name -> new name
michael@0 287 renames[key] = resolutions[key];
michael@0 288 else // name -> undefined
michael@0 289 exclusions.push(key);
michael@0 290 }
michael@0 291 return rename(renames, exclude(exclusions, trait));
michael@0 292 }
michael@0 293 exports.resolve = resolve;
michael@0 294
michael@0 295 /**
michael@0 296 * `create` is like `Object.create`, except that it ensures that:
michael@0 297 * - an exception is thrown if 'trait' still contains required properties
michael@0 298 * - an exception is thrown if 'trait' still contains conflicting
michael@0 299 * properties
michael@0 300 * @param {Object}
michael@0 301 * prototype of the completed object
michael@0 302 * @param {Object} trait
michael@0 303 * trait object to be turned into a complete object
michael@0 304 * @returns {Object}
michael@0 305 * An object with all of the properties described by the trait.
michael@0 306 */
michael@0 307 function create(proto, trait) {
michael@0 308 let properties = {},
michael@0 309 keys = getOwnPropertyNames(trait);
michael@0 310 for each(let key in keys) {
michael@0 311 let descriptor = trait[key];
michael@0 312 if (descriptor.required && !hasOwn.call(proto, key))
michael@0 313 throw new Error(ERR_REQUIRED + key);
michael@0 314 else if (descriptor.conflict)
michael@0 315 throw new Error(ERR_CONFLICT + key);
michael@0 316 else
michael@0 317 properties[key] = descriptor;
michael@0 318 }
michael@0 319 return _create(proto, properties);
michael@0 320 }
michael@0 321 exports.create = create;
michael@0 322

mercurial