Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 var Trait = require("sdk/deprecated/light-traits").Trait;
8 var utils = require("./utils");
9 var Data = utils.Data;
10 var Method = utils.Method;
11 var Accessor = utils.Accessor;
12 var Required = utils.Required;
13 var Conflict = utils.Conflict;
15 function method() {}
17 exports.Assert = require("./assert").Assert;
19 exports["test empty trait"] = function (assert) {
20 assert.equalTraits(Trait({}), {});
21 };
23 exports["test simple trait"] = function (assert) {
24 var expected = {
25 a: Data(0, true, true, true),
26 b: Method(method, true, true, true)
27 };
29 assert.equalTraits(Trait({ a: 0, b: method }), expected);
30 };
32 exports["test simple trait with Trait.required property"] = function (assert) {
33 var actual = Trait({ a: Trait.required, b: 1 });
34 var expected = { a: Required("a"), b: Data(1) };
36 assert.equalTraits(actual, expected);
37 };
39 exports["test ordering of trait properties is irrelevant"] = function (assert) {
40 var actual = Trait({ a: 0, b: 1, c: Trait.required });
41 var expected = Trait({ b: 1, c: Trait.required, a: 0 });
43 assert.equalTraits(actual, expected);
44 };
46 exports["test trait with accessor property"] = function (assert) {
47 var record = { get a() {}, set a(v) {} };
48 var get = Object.getOwnPropertyDescriptor(record, "a").get;
49 var set = Object.getOwnPropertyDescriptor(record, "a").set;
51 assert.equalTraits(Trait(record), { a: Accessor(get, set) });
52 };
54 exports["test simple composition"] = function (assert) {
55 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
56 var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) };
58 assert.equalTraits(actual, expected);
59 };
61 exports["test composition with conflict"] = function (assert) {
62 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method }));
63 var expected = { a: Conflict("a"), b: Data(1), c: Method(method) };
65 assert.equalTraits(actual, expected);
66 };
68 exports["test composition of identical props does not cause conflict"] = function (assert) {
69 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method }));
71 assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) });
72 };
74 exports["test composition with identical Trait.required props"] = function (assert) {
75 var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
76 Trait({ a: Trait.required, c: method }));
78 assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) });
79 };
81 exports["test composition satisfying a Trait.required prop"] = function (assert) {
82 var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
83 Trait({ a: method }));
85 assert.equalTraits(actual, { a: Method(method), b: Data(1) });
86 };
88 exports["test compose is neutral wrt conflicts"] = function (assert) {
89 var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })),
90 Trait({ b: 0 }));
92 assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) });
93 };
95 exports["test conflicting prop overrides Trait.required prop"] = function (assert) {
96 var actual = Trait.compose(Trait.compose(Trait({ a: 1 }),
97 Trait({ a: 2 })),
98 Trait({ a: Trait.required }));
100 assert.equalTraits(actual, { a: Conflict("a") });
101 };
103 exports["test compose is commutative"] = function (assert) {
104 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
105 var expected = Trait.compose(Trait({ c: 2, d: method }),
106 Trait({ a: 0, b: 1 }));
108 assert.equalTraits(actual, expected);
109 };
111 exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) {
112 var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }),
113 Trait({ c: 2, d: method }));
115 var expected = Trait.compose(Trait({ c: 2, d: method }),
116 Trait({ a: 0, b: 1, c: 3, e: Trait.required }));
118 assert.equalTraits(actual, expected);
119 };
121 exports["test compose is associative"] = function (assert) {
122 var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
123 Trait.compose(Trait({ c: 3, d: Trait.required }),
124 Trait({ c: 2, d: method,
125 e: "foo" })));
127 var expected = Trait.compose(
128 Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
129 Trait({ c: 3, d: Trait.required })),
130 Trait({ c: 2, d: method, e: "foo" }));
132 assert.equalTraits(actual, expected);
133 };
135 exports["test diamond import of same prop does not generate conflict"] = function (assert) {
136 var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })),
137 Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })),
138 Trait({ d: 4 }));
139 var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) };
141 assert.equalTraits(actual, expected);
142 };
144 exports["test resolve with empty resolutions has no effect"] = function (assert) {
145 assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}),
146 { a: Data(1), b: Required(), c: Method(method) });
147 };
149 exports["test resolve: renaming"] = function (assert) {
150 var actual = Trait({ a: 1, b: Trait.required, c: method });
152 assert.equalTraits(actual.resolve({ a: "A", c: "C" }),
153 { A: Data(1), b: Required(), C: Method(method),
154 a: Required(), c: Required() });
155 };
157 exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) {
158 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }),
159 { b: Conflict("b"), a: Required() });
160 };
162 exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) {
163 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }),
164 { b: Conflict("b"), a: Required() });
165 };
167 exports["test resolve: simple exclusion"] = function (assert) {
168 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }),
169 { a: Required(), b: Data(2) });
170 };
172 exports["test resolve: exclusion to empty trait"] = function (assert) {
173 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }),
174 { a: Required(), b: Required() });
175 };
177 exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) {
178 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }),
179 { a: Required(), c: Data(2), b: Required() });
180 };
182 exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) {
183 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }),
184 { a: Data(2), b: Required() });
185 };
187 exports["test resolve: renaming to a common alias causes conflict"] = function (assert) {
188 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }),
189 { c: Conflict("c"), a: Required(), b: Required() });
190 };
192 exports["test resolve: renaming overrides Trait.required target"] = function (assert) {
193 assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }),
194 { a: Data(2), b: Required() });
195 };
197 exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) {
198 assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }),
199 { a: Data(2), b: Required() });
200 };
202 exports["test resolve: renaming of non-existent props has no effect"] = function (assert) {
203 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }),
204 { c: Data(1), b: Data(2), a: Required() });
205 };
207 exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) {
208 assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) });
209 };
211 exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) {
212 var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 });
213 var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) };
214 assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected);
215 };
217 exports["test resolve supports swapping of property names, ordering 1"] = function (assert) {
218 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }),
219 { a: Data(2), b: Data(1) });
220 };
222 exports["test resolve supports swapping of property names, ordering 2"] = function (assert) {
223 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }),
224 { a: Data(2), b: Data(1) });
225 };
227 exports["test resolve supports swapping of property names, ordering 3"] = function (assert) {
228 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }),
229 { a: Data(2), b: Data(1) });
230 };
232 exports["test resolve supports swapping of property names, ordering 4"] = function (assert) {
233 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }),
234 { a: Data(2), b: Data(1) });
235 };
237 exports["test create simple"] = function (assert) {
238 var o1 = Trait({
239 a: 1,
240 b: function () {
241 return this.a;
242 }
243 }).create(Object.prototype);
245 assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype");
246 assert.equal(1, o1.a, "o1.a");
247 assert.equal(1, o1.b(), "o1.b()");
248 assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2");
249 };
251 exports["test create with Array.prototype"] = function (assert) {
252 var o2 = Trait({}).create(Array.prototype);
253 assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype");
254 };
256 exports["test exception for incomplete required properties"] = function (assert) {
257 assert.throws(function () {
258 Trait({ foo: Trait.required }).create(Object.prototype);
259 }, /Missing required property: `foo`/, "required prop error");
260 };
262 exports["test exception for unresolved conflicts"] = function (assert) {
263 assert.throws(function () {
264 Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({});
265 }, /Remaining conflicting property: `a`/, "conflicting prop error");
266 };
268 exports["test verify that required properties are present but undefined"] = function (assert) {
269 var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required }));
271 assert.ok("foo" in o4, "required property present");
272 assert.throws(function () {
273 o4.foo;
274 }, /Missing required property: `foo`/, "required prop error");
275 };
277 exports["test verify that conflicting properties are present"] = function (assert) {
278 var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }),
279 Trait({ a: 1 })));
281 assert.ok("a" in o5, "conflicting property present");
282 assert.throws(function () {
283 o5.a;
284 }, /Remaining conflicting property: `a`/, "conflicting prop access error");
285 };
287 exports["test diamond with conflicts"] = function (assert) {
288 function makeT1(x) {
289 return Trait({
290 m: function () {
291 return x
292 }
293 })
294 };
296 function makeT2(x) {
297 return Trait.compose(Trait({
298 t2: "foo"
299 }), makeT1(x));
300 };
302 function makeT3(x) {
303 return Trait.compose(Trait({
304 t3: "bar"
305 }), makeT1(x));
306 };
308 var T4 = Trait.compose(makeT2(5), makeT3(5));
310 assert.throws(function () {
311 T4.create(Object.prototype);
312 }, /Remaining conflicting property: `m`/, "diamond prop conflict");
313 };
315 exports["test providing requirements through proto"] = function (assert) {
316 var t = Trait({ required: Trait.required }).create({ required: "test" });
317 assert.equal(t.required, "test", "property from proto is inherited");
318 };
320 if (module == require.main)
321 require("test").run(exports);