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 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 let tests = [
8 {
9 desc: "nsNavHistoryFolderResultNode: Basic test, asynchronously open and " +
10 "close container with a single child",
12 loading: function (node, newState, oldState) {
13 this.checkStateChanged("loading", 1);
14 this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
15 },
17 opened: function (node, newState, oldState) {
18 this.checkStateChanged("opened", 1);
19 this.checkState("loading", 1);
20 this.checkArgs("opened", node, oldState, node.STATE_LOADING);
22 print("Checking node children");
23 compareArrayToResult(this.data, node);
25 print("Closing container");
26 node.containerOpen = false;
27 },
29 closed: function (node, newState, oldState) {
30 this.checkStateChanged("closed", 1);
31 this.checkState("opened", 1);
32 this.checkArgs("closed", node, oldState, node.STATE_OPENED);
33 this.success();
34 }
35 },
37 {
38 desc: "nsNavHistoryFolderResultNode: After async open and no changes, " +
39 "second open should be synchronous",
41 loading: function (node, newState, oldState) {
42 this.checkStateChanged("loading", 1);
43 this.checkState("closed", 0);
44 this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
45 },
47 opened: function (node, newState, oldState) {
48 let cnt = this.checkStateChanged("opened", 1, 2);
49 let expectOldState = cnt === 1 ? node.STATE_LOADING : node.STATE_CLOSED;
50 this.checkArgs("opened", node, oldState, expectOldState);
52 print("Checking node children");
53 compareArrayToResult(this.data, node);
55 print("Closing container");
56 node.containerOpen = false;
57 },
59 closed: function (node, newState, oldState) {
60 let cnt = this.checkStateChanged("closed", 1, 2);
61 this.checkArgs("closed", node, oldState, node.STATE_OPENED);
63 switch (cnt) {
64 case 1:
65 node.containerOpen = true;
66 break;
67 case 2:
68 this.success();
69 break;
70 }
71 }
72 },
74 {
75 desc: "nsNavHistoryFolderResultNode: After closing container in " +
76 "loading(), opened() should not be called",
78 loading: function (node, newState, oldState) {
79 this.checkStateChanged("loading", 1);
80 this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
81 print("Closing container");
82 node.containerOpen = false;
83 },
85 opened: function (node, newState, oldState) {
86 do_throw("opened should not be called");
87 },
89 closed: function (node, newState, oldState) {
90 this.checkStateChanged("closed", 1);
91 this.checkState("loading", 1);
92 this.checkArgs("closed", node, oldState, node.STATE_LOADING);
93 this.success();
94 }
95 }
96 ];
99 /**
100 * Instances of this class become the prototypes of the test objects above.
101 * Each test can therefore use the methods of this class, or they can override
102 * them if they want. To run a test, call setup() and then run().
103 */
104 function Test() {
105 // This maps a state name to the number of times it's been observed.
106 this.stateCounts = {};
107 // Promise object resolved when the next test can be run.
108 this.deferNextTest = Promise.defer();
109 }
111 Test.prototype = {
112 /**
113 * Call this when an observer observes a container state change to sanity
114 * check the arguments.
115 *
116 * @param aNewState
117 * The name of the new state. Used only for printing out helpful info.
118 * @param aNode
119 * The node argument passed to containerStateChanged.
120 * @param aOldState
121 * The old state argument passed to containerStateChanged.
122 * @param aExpectOldState
123 * The expected old state.
124 */
125 checkArgs: function (aNewState, aNode, aOldState, aExpectOldState) {
126 print("Node passed on " + aNewState + " should be result.root");
127 do_check_eq(this.result.root, aNode);
128 print("Old state passed on " + aNewState + " should be " + aExpectOldState);
130 // aOldState comes from xpconnect and will therefore be defined. It may be
131 // zero, though, so use strict equality just to make sure aExpectOldState is
132 // also defined.
133 do_check_true(aOldState === aExpectOldState);
134 },
136 /**
137 * Call this when an observer observes a container state change. It registers
138 * the state change and ensures that it has been observed the given number
139 * of times. See checkState for parameter explanations.
140 *
141 * @return The number of times aState has been observed, including the new
142 * observation.
143 */
144 checkStateChanged: function (aState, aExpectedMin, aExpectedMax) {
145 print(aState + " state change observed");
146 if (!this.stateCounts.hasOwnProperty(aState))
147 this.stateCounts[aState] = 0;
148 this.stateCounts[aState]++;
149 return this.checkState(aState, aExpectedMin, aExpectedMax);
150 },
152 /**
153 * Ensures that the state has been observed the given number of times.
154 *
155 * @param aState
156 * The name of the state.
157 * @param aExpectedMin
158 * The state must have been observed at least this number of times.
159 * @param aExpectedMax
160 * The state must have been observed at most this number of times.
161 * This parameter is optional. If undefined, it's set to
162 * aExpectedMin.
163 * @return The number of times aState has been observed, including the new
164 * observation.
165 */
166 checkState: function (aState, aExpectedMin, aExpectedMax) {
167 let cnt = this.stateCounts[aState] || 0;
168 if (aExpectedMax === undefined)
169 aExpectedMax = aExpectedMin;
170 if (aExpectedMin === aExpectedMax) {
171 print(aState + " should be observed only " + aExpectedMin +
172 " times (actual = " + cnt + ")");
173 }
174 else {
175 print(aState + " should be observed at least " + aExpectedMin +
176 " times and at most " + aExpectedMax + " times (actual = " +
177 cnt + ")");
178 }
179 do_check_true(cnt >= aExpectedMin && cnt <= aExpectedMax);
180 return cnt;
181 },
183 /**
184 * Asynchronously opens the root of the test's result.
185 */
186 openContainer: function () {
187 // Set up the result observer. It delegates to this object's callbacks and
188 // wraps them in a try-catch so that errors don't get eaten.
189 this.observer = let (self = this) {
190 containerStateChanged: function (container, oldState, newState) {
191 print("New state passed to containerStateChanged() should equal the " +
192 "container's current state");
193 do_check_eq(newState, container.state);
195 try {
196 switch (newState) {
197 case Ci.nsINavHistoryContainerResultNode.STATE_LOADING:
198 self.loading(container, newState, oldState);
199 break;
200 case Ci.nsINavHistoryContainerResultNode.STATE_OPENED:
201 self.opened(container, newState, oldState);
202 break;
203 case Ci.nsINavHistoryContainerResultNode.STATE_CLOSED:
204 self.closed(container, newState, oldState);
205 break;
206 default:
207 do_throw("Unexpected new state! " + newState);
208 }
209 }
210 catch (err) {
211 do_throw(err);
212 }
213 },
214 };
215 this.result.addObserver(this.observer, false);
217 print("Opening container");
218 this.result.root.containerOpen = true;
219 },
221 /**
222 * Starts the test and returns a promise resolved when the test completes.
223 */
224 run: function () {
225 this.openContainer();
226 return this.deferNextTest.promise;
227 },
229 /**
230 * This must be called before run(). It adds a bookmark and sets up the
231 * test's result. Override if need be.
232 */
233 setup: function () {
234 // Populate the database with different types of bookmark items.
235 this.data = DataHelper.makeDataArray([
236 { type: "bookmark" },
237 { type: "separator" },
238 { type: "folder" },
239 { type: "bookmark", uri: "place:terms=foo" }
240 ]);
241 yield task_populateDB(this.data);
243 // Make a query.
244 this.query = PlacesUtils.history.getNewQuery();
245 this.query.setFolders([DataHelper.defaults.bookmark.parent], 1);
246 this.opts = PlacesUtils.history.getNewQueryOptions();
247 this.opts.asyncEnabled = true;
248 this.result = PlacesUtils.history.executeQuery(this.query, this.opts);
249 },
251 /**
252 * Call this when the test has succeeded. It cleans up resources and starts
253 * the next test.
254 */
255 success: function () {
256 this.result.removeObserver(this.observer);
258 // Resolve the promise object that indicates that the next test can be run.
259 this.deferNextTest.resolve();
260 }
261 };
263 /**
264 * This makes it a little bit easier to use the functions of head_queries.js.
265 */
266 let DataHelper = {
267 defaults: {
268 bookmark: {
269 parent: PlacesUtils.bookmarks.unfiledBookmarksFolder,
270 uri: "http://example.com/",
271 title: "test bookmark"
272 },
274 folder: {
275 parent: PlacesUtils.bookmarks.unfiledBookmarksFolder,
276 title: "test folder"
277 },
279 separator: {
280 parent: PlacesUtils.bookmarks.unfiledBookmarksFolder
281 }
282 },
284 /**
285 * Converts an array of simple bookmark item descriptions to the more verbose
286 * format required by task_populateDB() in head_queries.js.
287 *
288 * @param aData
289 * An array of objects, each of which describes a bookmark item.
290 * @return An array of objects suitable for passing to populateDB().
291 */
292 makeDataArray: function DH_makeDataArray(aData) {
293 return let (self = this) aData.map(function (dat) {
294 let type = dat.type;
295 dat = self._makeDataWithDefaults(dat, self.defaults[type]);
296 switch (type) {
297 case "bookmark":
298 return {
299 isBookmark: true,
300 uri: dat.uri,
301 parentFolder: dat.parent,
302 index: PlacesUtils.bookmarks.DEFAULT_INDEX,
303 title: dat.title,
304 isInQuery: true
305 };
306 case "separator":
307 return {
308 isSeparator: true,
309 parentFolder: dat.parent,
310 index: PlacesUtils.bookmarks.DEFAULT_INDEX,
311 isInQuery: true
312 };
313 case "folder":
314 return {
315 isFolder: true,
316 readOnly: false,
317 parentFolder: dat.parent,
318 index: PlacesUtils.bookmarks.DEFAULT_INDEX,
319 title: dat.title,
320 isInQuery: true
321 };
322 default:
323 do_throw("Unknown data type when populating DB: " + type);
324 }
325 });
326 },
328 /**
329 * Returns a copy of aData, except that any properties that are undefined but
330 * defined in aDefaults are set to the corresponding values in aDefaults.
331 *
332 * @param aData
333 * An object describing a bookmark item.
334 * @param aDefaults
335 * An object describing the default bookmark item.
336 * @return A copy of aData with defaults values set.
337 */
338 _makeDataWithDefaults: function DH__makeDataWithDefaults(aData, aDefaults) {
339 let dat = {};
340 for (let [prop, val] in Iterator(aDefaults)) {
341 dat[prop] = aData.hasOwnProperty(prop) ? aData[prop] : val;
342 }
343 return dat;
344 }
345 };
347 function run_test()
348 {
349 run_next_test();
350 }
352 add_task(function test_async()
353 {
354 for (let [, test] in Iterator(tests)) {
355 remove_all_bookmarks();
357 test.__proto__ = new Test();
358 yield test.setup();
360 print("------ Running test: " + test.desc);
361 yield test.run();
362 }
364 remove_all_bookmarks();
365 print("All tests done, exiting");
366 });