Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef MERGED_COMPARTMENT
7 this.EXPORTED_SYMBOLS = ["Async"];
9 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
11 #endif
13 // Constants for makeSyncCallback, waitForSyncCallback.
14 const CB_READY = {};
15 const CB_COMPLETE = {};
16 const CB_FAIL = {};
18 const REASON_ERROR = Ci.mozIStorageStatementCallback.REASON_ERROR;
20 Cu.import("resource://gre/modules/Services.jsm");
22 /*
23 * Helpers for various async operations.
24 */
25 this.Async = {
27 /**
28 * Execute an arbitrary number of asynchronous functions one after the
29 * other, passing the callback arguments on to the next one. All functions
30 * must take a callback function as their last argument. The 'this' object
31 * will be whatever chain()'s is.
32 *
33 * @usage this._chain = Async.chain;
34 * this._chain(this.foo, this.bar, this.baz)(args, for, foo)
35 *
36 * This is equivalent to:
37 *
38 * let self = this;
39 * self.foo(args, for, foo, function (bars, args) {
40 * self.bar(bars, args, function (baz, params) {
41 * self.baz(baz, params);
42 * });
43 * });
44 */
45 chain: function chain() {
46 let funcs = Array.slice(arguments);
47 let thisObj = this;
48 return function callback() {
49 if (funcs.length) {
50 let args = Array.slice(arguments).concat(callback);
51 let f = funcs.shift();
52 f.apply(thisObj, args);
53 }
54 };
55 },
57 /**
58 * Helpers for making asynchronous calls within a synchronous API possible.
59 *
60 * If you value your sanity, do not look closely at the following functions.
61 */
63 /**
64 * Create a sync callback that remembers state, in particular whether it has
65 * been called.
66 */
67 makeSyncCallback: function makeSyncCallback() {
68 // The main callback remembers the value it was passed, and that it got data.
69 let onComplete = function onComplete(data) {
70 onComplete.state = CB_COMPLETE;
71 onComplete.value = data;
72 };
74 // Initialize private callback data in preparation for being called.
75 onComplete.state = CB_READY;
76 onComplete.value = null;
78 // Allow an alternate callback to trigger an exception to be thrown.
79 onComplete.throw = function onComplete_throw(data) {
80 onComplete.state = CB_FAIL;
81 onComplete.value = data;
83 // Cause the caller to get an exception and stop execution.
84 throw data;
85 };
87 return onComplete;
88 },
90 /**
91 * Wait for a sync callback to finish.
92 */
93 waitForSyncCallback: function waitForSyncCallback(callback) {
94 // Grab the current thread so we can make it give up priority.
95 let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
97 // Keep waiting until our callback is triggered (unless the app is quitting).
98 while (Async.checkAppReady() && callback.state == CB_READY) {
99 thread.processNextEvent(true);
100 }
102 // Reset the state of the callback to prepare for another call.
103 let state = callback.state;
104 callback.state = CB_READY;
106 // Throw the value the callback decided to fail with.
107 if (state == CB_FAIL) {
108 throw callback.value;
109 }
111 // Return the value passed to the callback.
112 return callback.value;
113 },
115 /**
116 * Check if the app is still ready (not quitting).
117 */
118 checkAppReady: function checkAppReady() {
119 // Watch for app-quit notification to stop any sync calls
120 Services.obs.addObserver(function onQuitApplication() {
121 Services.obs.removeObserver(onQuitApplication, "quit-application");
122 Async.checkAppReady = function() {
123 throw Components.Exception("App. Quitting", Cr.NS_ERROR_ABORT);
124 };
125 }, "quit-application", false);
126 // In the common case, checkAppReady just returns true
127 return (Async.checkAppReady = function() { return true; })();
128 },
130 /**
131 * Return the two things you need to make an asynchronous call synchronous
132 * by spinning the event loop.
133 */
134 makeSpinningCallback: function makeSpinningCallback() {
135 let cb = Async.makeSyncCallback();
136 function callback(error, ret) {
137 if (error)
138 cb.throw(error);
139 cb(ret);
140 }
141 callback.wait = function() Async.waitForSyncCallback(cb);
142 return callback;
143 },
145 // Prototype for mozIStorageCallback, used in querySpinningly.
146 // This allows us to define the handle* functions just once rather
147 // than on every querySpinningly invocation.
148 _storageCallbackPrototype: {
149 results: null,
151 // These are set by queryAsync.
152 names: null,
153 syncCb: null,
155 handleResult: function handleResult(results) {
156 if (!this.names) {
157 return;
158 }
159 if (!this.results) {
160 this.results = [];
161 }
162 let row;
163 while ((row = results.getNextRow()) != null) {
164 let item = {};
165 for each (let name in this.names) {
166 item[name] = row.getResultByName(name);
167 }
168 this.results.push(item);
169 }
170 },
171 handleError: function handleError(error) {
172 this.syncCb.throw(error);
173 },
174 handleCompletion: function handleCompletion(reason) {
176 // If we got an error, handleError will also have been called, so don't
177 // call the callback! We never cancel statements, so we don't need to
178 // address that quandary.
179 if (reason == REASON_ERROR)
180 return;
182 // If we were called with column names but didn't find any results,
183 // the calling code probably still expects an array as a return value.
184 if (this.names && !this.results) {
185 this.results = [];
186 }
187 this.syncCb(this.results);
188 }
189 },
191 querySpinningly: function querySpinningly(query, names) {
192 // 'Synchronously' asyncExecute, fetching all results by name.
193 let storageCallback = {names: names,
194 syncCb: Async.makeSyncCallback()};
195 storageCallback.__proto__ = Async._storageCallbackPrototype;
196 query.executeAsync(storageCallback);
197 return Async.waitForSyncCallback(storageCallback.syncCb);
198 },
199 };