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.

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

mercurial