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.
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;