Skip to content

Commit 5517c22

Browse files
committed
prevented race conditions while restoring connections
1 parent 3721531 commit 5517c22

3 files changed

Lines changed: 111 additions & 11 deletions

File tree

build/bgscript.js

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ exports.default = void 0;
1818
var _CustomEventTarget = _interopRequireDefault(require("./CustomEventTarget.js"));
1919
var _Connection = require("./Connection.js");
2020
var _Errors = require("./Errors.js");
21+
var _utilities = require("./utilities.js");
2122
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2223
const STORED_CONNECTIONS_KEY = "bgscript.connections";
2324

@@ -46,21 +47,58 @@ class BackgroundHandler extends _CustomEventTarget.default {
4647
this.runtime = (_options$runtime = options.runtime) !== null && _options$runtime !== void 0 ? _options$runtime : chrome.runtime;
4748
this.storage = (_options$storage = options.storage) !== null && _options$storage !== void 0 ? _options$storage : chrome.storage;
4849
this.chromeTabs = (_options$chromeTabs = options.chromeTabs) !== null && _options$chromeTabs !== void 0 ? _options$chromeTabs : chrome.tabs;
50+
51+
/** @property {bool} isRestoringConnections */
52+
this.isRestoringConnections = false;
4953
this.runtime.onConnect.addListener(port => this.handleNewConnection(port));
5054
this.restoreConnections();
5155
}
56+
57+
/**
58+
* Restore all the connections that were saved in the storage.
59+
*/
5260
async restoreConnections() {
61+
console.log("Restoring scripts connections");
62+
this.isRestoringConnections = true;
5363
let data = await this.storage.local.get(STORED_CONNECTIONS_KEY);
64+
let connected = [];
5465
if (data[STORED_CONNECTIONS_KEY]) {
5566
let connections = data[STORED_CONNECTIONS_KEY];
5667
for (let [scriptId, tab] of connections) {
57-
this.chromeTabs.sendMessage(tab, {
58-
type: _Connection.CONNECTION_PREFIX + "ping",
59-
scriptId
60-
});
68+
try {
69+
await this.chromeTabs.sendMessage(tab, {
70+
type: _Connection.CONNECTION_PREFIX + "ping",
71+
scriptId
72+
});
73+
connected.push([scriptId, tab]);
74+
} catch (_unused) {
75+
console.log(`Could not connect to tab ${tab}. Skipping.`);
76+
}
6177
}
6278
}
79+
await this.storage.local.set({
80+
[STORED_CONNECTIONS_KEY]: connected
81+
});
82+
this.isRestoringConnections = false;
83+
console.log("Finished restoring connections");
84+
}
85+
86+
/**
87+
* Waits for all connections to be restored. Useful to avoid returning
88+
* the wrong number of connections from the "getScriptConnection" of
89+
* "getScriptTabs" functions.
90+
*
91+
* @returns {Promise<void>} A promise that is resolved when the restoration is finished
92+
*/
93+
async waitForRestoration() {
94+
while (this.isRestoringConnections) {
95+
await (0, _utilities.waitForNextTask)();
96+
}
6397
}
98+
99+
/**
100+
* Save the current connections in the storage
101+
*/
64102
async saveConnections() {
65103
let conns = [];
66104
for (let [scriptId, _] of this.scriptConnections) {
@@ -193,6 +231,7 @@ class BackgroundHandler extends _CustomEventTarget.default {
193231
* @return {Promise<Proxy>} The connection proxy
194232
*/
195233
async getScriptConnection(scriptId, tabId) {
234+
await this.waitForRestoration();
196235
let specificScriptId = scriptId;
197236
if (tabId) specificScriptId += `-${tabId}`;
198237
let connection = this.scriptConnections.get(specificScriptId);
@@ -210,7 +249,8 @@ class BackgroundHandler extends _CustomEventTarget.default {
210249
* @param {string} scriptId
211250
* @returns {number[]} The tab IDs
212251
*/
213-
getScriptTabs(scriptId) {
252+
async getScriptTabs(scriptId) {
253+
await this.waitForRestoration();
214254
let tabs = [];
215255
for (let [id, connection] of this.scriptConnections) {
216256
if (id.startsWith(scriptId)) {
@@ -256,7 +296,7 @@ class BackgroundHandler extends _CustomEventTarget.default {
256296
}
257297
var _default = exports.default = BackgroundHandler;
258298

259-
},{"./Connection.js":4,"./CustomEventTarget.js":5,"./Errors.js":6}],3:[function(require,module,exports){
299+
},{"./Connection.js":4,"./CustomEventTarget.js":5,"./Errors.js":6,"./utilities.js":7}],3:[function(require,module,exports){
260300
"use strict";
261301

262302
Object.defineProperty(exports, "__esModule", {
@@ -871,4 +911,17 @@ const BgHandlerErrors = exports.BgHandlerErrors = {
871911
NO_CONNECTION: new Error('NO_CONNECTION', (scriptId, tabId) => `There is no connection assigned to id '${scriptId}'${tabId ? ` connected to the tab ${tabId}` : ''}.`)
872912
};
873913

914+
},{}],7:[function(require,module,exports){
915+
"use strict";
916+
917+
Object.defineProperty(exports, "__esModule", {
918+
value: true
919+
});
920+
exports.waitForNextTask = waitForNextTask;
921+
function waitForNextTask() {
922+
return new Promise(resolve => {
923+
setTimeout(resolve, 0);
924+
});
925+
}
926+
874927
},{}]},{},[1]);

src/BackgroundHandler.js

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import CustomEventTarget from './CustomEventTarget.js';
22
import { Connection, CONNECTION_PREFIX, CONNECTION_PREFIX_NOTAB } from './Connection.js';
33
import { BgHandlerErrors as ERRORS, Error } from './Errors.js';
4+
import { waitForNextTask } from './utilities.js';
45

56
const STORED_CONNECTIONS_KEY = "bgscript.connections";
67

@@ -31,26 +32,63 @@ class BackgroundHandler extends CustomEventTarget {
3132
this.storage = options.storage ?? chrome.storage;
3233
this.chromeTabs = options.chromeTabs ?? chrome.tabs;
3334

35+
/** @property {bool} isRestoringConnections */
36+
this.isRestoringConnections = false;
37+
3438
this.runtime.onConnect.addListener( (port) => this.handleNewConnection(port) );
3539

3640
this.restoreConnections();
3741
}
3842

43+
/**
44+
* Restore all the connections that were saved in the storage.
45+
*/
3946
async restoreConnections() {
47+
console.log("Restoring scripts connections");
48+
this.isRestoringConnections = true;
49+
4050
let data = await this.storage.local.get(STORED_CONNECTIONS_KEY);
51+
let connected = [];
4152

4253
if (data[STORED_CONNECTIONS_KEY]) {
4354
let connections = data[STORED_CONNECTIONS_KEY];
4455

4556
for (let [scriptId, tab] of connections) {
46-
this.chromeTabs.sendMessage(tab, {
47-
type: CONNECTION_PREFIX + "ping",
48-
scriptId
49-
});
57+
try {
58+
await this.chromeTabs.sendMessage(tab, {
59+
type: CONNECTION_PREFIX + "ping",
60+
scriptId
61+
});
62+
63+
connected.push([scriptId, tab]);
64+
} catch {
65+
console.log(`Could not connect to tab ${tab}. Skipping.`);
66+
}
5067
}
5168
}
69+
70+
await this.storage.local.set({ [STORED_CONNECTIONS_KEY]: connected });
71+
72+
this.isRestoringConnections = false;
73+
console.log("Finished restoring connections");
5274
}
5375

76+
/**
77+
* Waits for all connections to be restored. Useful to avoid returning
78+
* the wrong number of connections from the "getScriptConnection" of
79+
* "getScriptTabs" functions.
80+
*
81+
* @returns {Promise<void>} A promise that is resolved when the restoration is finished
82+
*/
83+
async waitForRestoration() {
84+
while (this.isRestoringConnections) {
85+
await waitForNextTask();
86+
}
87+
}
88+
89+
/**
90+
* Save the current connections in the storage
91+
*/
5492
async saveConnections() {
5593
let conns = [];
5694
for (let [scriptId, _] of this.scriptConnections) {
@@ -188,6 +226,8 @@ class BackgroundHandler extends CustomEventTarget {
188226
*/
189227
async getScriptConnection(scriptId, tabId) {
190228

229+
await this.waitForRestoration();
230+
191231
let specificScriptId = scriptId;
192232

193233
if (tabId) specificScriptId += `-${tabId}`;
@@ -210,7 +250,9 @@ class BackgroundHandler extends CustomEventTarget {
210250
* @param {string} scriptId
211251
* @returns {number[]} The tab IDs
212252
*/
213-
getScriptTabs(scriptId) {
253+
async getScriptTabs(scriptId) {
254+
await this.waitForRestoration();
255+
214256
let tabs = [];
215257
for (let [id, connection] of this.scriptConnections) {
216258
if (id.startsWith(scriptId)) {

src/utilities.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function waitForNextTask() {
2+
return new Promise((resolve) => {
3+
setTimeout(resolve, 0);
4+
});
5+
}

0 commit comments

Comments
 (0)