chrome-in-iframe 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/CHANGELOG.zh-CN.md +104 -7
- package/README.md +12 -10
- package/README.zh-CN.md +13 -11
- package/dist/api.d.ts +2 -0
- package/dist/channel/channel.d.ts +6 -2
- package/dist/channel/deserializer.d.ts +7 -1
- package/dist/channel/listener.d.ts +1 -1
- package/dist/channel/path.d.ts +1 -1
- package/dist/channel/sender.d.ts +1 -1
- package/dist/channel/serializer.d.ts +4 -1
- package/dist/channel/types.d.ts +23 -4
- package/dist/channel/utils.d.ts +4 -1
- package/dist/client/context.d.ts +1 -1
- package/dist/client/endpoint.d.ts +3 -2
- package/dist/client/proxy.d.ts +1 -1
- package/dist/index.cjs +2153 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +937 -563
- package/dist/processor/accessProperty.d.ts +4 -4
- package/dist/processor/helpers.d.ts +21 -0
- package/dist/processor/invoke.d.ts +4 -4
- package/dist/processor/invokeCallback.d.ts +4 -4
- package/dist/processor/lifecycle.d.ts +4 -2
- package/dist/processor/readProperty.d.ts +1 -1
- package/dist/processor/registry.d.ts +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { LRUCache } from 'lru-cache';
|
|
3
3
|
|
|
4
|
+
const TRANSPORT_DETACHED = Symbol.for('chrome-in-iframe.transport-detached');
|
|
5
|
+
function isTransportDetachedError(err) {
|
|
6
|
+
return err instanceof Error && err[TRANSPORT_DETACHED] === true;
|
|
7
|
+
}
|
|
8
|
+
function createTransportDetachedError(message) {
|
|
9
|
+
const err = new Error(message);
|
|
10
|
+
Object.defineProperty(err, TRANSPORT_DETACHED, { value: true, enumerable: false });
|
|
11
|
+
return err;
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
const PREFIX = 'chrome-in-iframe';
|
|
5
15
|
const defaultLogger = (scope, ...args) => {
|
|
6
16
|
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
|
|
@@ -39,6 +49,34 @@ function getWellKnownSymbolName(value) {
|
|
|
39
49
|
function getWellKnownSymbol(name) {
|
|
40
50
|
return WELL_KNOWN_SYMBOLS[name];
|
|
41
51
|
}
|
|
52
|
+
function encodeSymbolToken(value, context) {
|
|
53
|
+
const globalKey = Symbol.keyFor(value);
|
|
54
|
+
if (globalKey)
|
|
55
|
+
return `global:${globalKey}`;
|
|
56
|
+
const wellKnownName = getWellKnownSymbolName(value);
|
|
57
|
+
if (wellKnownName)
|
|
58
|
+
return `wellKnown:${wellKnownName}`;
|
|
59
|
+
throw new TypeError(context ? `Cannot serialize non-global symbol ${context}` : 'Cannot serialize non-global symbol');
|
|
60
|
+
}
|
|
61
|
+
function decodeSymbolToken(value, context) {
|
|
62
|
+
if (value.startsWith('global:')) {
|
|
63
|
+
return Symbol.for(value.slice('global:'.length));
|
|
64
|
+
}
|
|
65
|
+
if (value.startsWith('wellKnown:')) {
|
|
66
|
+
const symbol = getWellKnownSymbol(value.slice('wellKnown:'.length));
|
|
67
|
+
if (symbol)
|
|
68
|
+
return symbol;
|
|
69
|
+
}
|
|
70
|
+
throw new TypeError(context ? `Cannot deserialize symbol ${context}` : 'Cannot deserialize symbol');
|
|
71
|
+
}
|
|
72
|
+
function setOwnProperty(target, key, value) {
|
|
73
|
+
Object.defineProperty(target, key, {
|
|
74
|
+
value,
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
42
80
|
function serializeThrownError(err) {
|
|
43
81
|
if (err instanceof Error) {
|
|
44
82
|
return { message: err.message, stack: err.stack };
|
|
@@ -57,7 +95,7 @@ function serializePath(path) {
|
|
|
57
95
|
return key;
|
|
58
96
|
return {
|
|
59
97
|
$type: 'symbol',
|
|
60
|
-
value:
|
|
98
|
+
value: encodeSymbolToken(key, 'path key'),
|
|
61
99
|
};
|
|
62
100
|
});
|
|
63
101
|
}
|
|
@@ -65,7 +103,7 @@ function deserializePath(path) {
|
|
|
65
103
|
return path.map((key) => {
|
|
66
104
|
if (typeof key === 'string')
|
|
67
105
|
return key;
|
|
68
|
-
return
|
|
106
|
+
return decodeSymbolToken(key.value, 'path key');
|
|
69
107
|
});
|
|
70
108
|
}
|
|
71
109
|
function isSerializedPath(value) {
|
|
@@ -79,26 +117,6 @@ function isSerializedPathKey(value) {
|
|
|
79
117
|
const record = value;
|
|
80
118
|
return record.$type === 'symbol' && typeof record.value === 'string';
|
|
81
119
|
}
|
|
82
|
-
function serializeSymbol$1(value) {
|
|
83
|
-
const globalKey = Symbol.keyFor(value);
|
|
84
|
-
if (globalKey)
|
|
85
|
-
return `global:${globalKey}`;
|
|
86
|
-
const wellKnownName = getWellKnownSymbolName(value);
|
|
87
|
-
if (wellKnownName)
|
|
88
|
-
return `wellKnown:${wellKnownName}`;
|
|
89
|
-
throw new TypeError('Cannot serialize non-global symbol path key');
|
|
90
|
-
}
|
|
91
|
-
function deserializeSymbol$1(value) {
|
|
92
|
-
if (value.startsWith('global:')) {
|
|
93
|
-
return Symbol.for(value.slice('global:'.length));
|
|
94
|
-
}
|
|
95
|
-
if (value.startsWith('wellKnown:')) {
|
|
96
|
-
const symbol = getWellKnownSymbol(value.slice('wellKnown:'.length));
|
|
97
|
-
if (symbol)
|
|
98
|
-
return symbol;
|
|
99
|
-
}
|
|
100
|
-
throw new TypeError('Cannot deserialize symbol path key');
|
|
101
|
-
}
|
|
102
120
|
|
|
103
121
|
function createMessageSender(poster, key, instanceId) {
|
|
104
122
|
return {
|
|
@@ -109,7 +127,7 @@ function createMessageSender(poster, key, instanceId) {
|
|
|
109
127
|
data: serializeMessageData(type, data),
|
|
110
128
|
senderInstanceId: instanceId,
|
|
111
129
|
};
|
|
112
|
-
if (targetInstanceId)
|
|
130
|
+
if (targetInstanceId !== undefined)
|
|
113
131
|
body.targetInstanceId = targetInstanceId;
|
|
114
132
|
poster.postMessage(JSON.stringify(body));
|
|
115
133
|
},
|
|
@@ -133,14 +151,47 @@ function serializeMessageData(type, data) {
|
|
|
133
151
|
return data;
|
|
134
152
|
}
|
|
135
153
|
|
|
154
|
+
const MAX_PATH_LENGTH = 64;
|
|
155
|
+
const MAX_INVOKE_ARGS = 1024;
|
|
156
|
+
const MAX_MESSAGE_LENGTH = 1000000;
|
|
157
|
+
const MAX_RELEASE_IDS = 10000;
|
|
158
|
+
const MAX_ID_LENGTH = 200;
|
|
159
|
+
const MAX_TYPE_LENGTH = 64;
|
|
160
|
+
const MESSAGE_SPECS = {
|
|
161
|
+
connectRequest: { validate: isConnectMessage },
|
|
162
|
+
connectResponse: { validate: isConnectMessage },
|
|
163
|
+
invokeRequest: {
|
|
164
|
+
validate: isInvokeRequest,
|
|
165
|
+
preDeserialize: (data) => ({ ...data, path: deserializePath(data.path) }),
|
|
166
|
+
errorResponse: { type: 'invokeResponse', extractId: (data) => data.id },
|
|
167
|
+
},
|
|
168
|
+
invokeResponse: { validate: isResponse },
|
|
169
|
+
accessPropertyRequest: {
|
|
170
|
+
validate: isAccessPropertyRequest,
|
|
171
|
+
preDeserialize: (data) => ({ ...data, path: deserializePath(data.path) }),
|
|
172
|
+
errorResponse: { type: 'accessPropertyResponse', extractId: (data) => data.id },
|
|
173
|
+
},
|
|
174
|
+
accessPropertyResponse: { validate: isResponse },
|
|
175
|
+
invokeFunctionByIdRequest: {
|
|
176
|
+
validate: isCallbackRequest,
|
|
177
|
+
errorResponse: { type: 'invokeFunctionByIdResponse', extractId: (data) => data.callId },
|
|
178
|
+
},
|
|
179
|
+
invokeFunctionByIdResponse: { validate: isResponse },
|
|
180
|
+
releaseCallbacks: { validate: isReleaseCallbacks },
|
|
181
|
+
destroyEndpoint: { validate: isDestroyEndpoint },
|
|
182
|
+
};
|
|
136
183
|
function createMessageChannel(poster, key, instanceId, context, processorRegistry) {
|
|
137
184
|
const sender = createMessageSender(poster, key, instanceId);
|
|
138
185
|
const keyMatch = `"key":${JSON.stringify(key)}`;
|
|
139
186
|
const listener = (event) => {
|
|
140
187
|
if (typeof event.data !== 'string')
|
|
141
188
|
return;
|
|
142
|
-
if (event.data.length === 0 || event.data.charCodeAt(0) !== 123
|
|
189
|
+
if (event.data.length === 0 || event.data.charCodeAt(0) !== 123)
|
|
190
|
+
return;
|
|
191
|
+
if (event.data.length > MAX_MESSAGE_LENGTH) {
|
|
192
|
+
warn('createMessageChannel', `dropping oversized message (${event.data.length} > ${MAX_MESSAGE_LENGTH})`);
|
|
143
193
|
return;
|
|
194
|
+
}
|
|
144
195
|
if (event.data.indexOf(keyMatch) === -1)
|
|
145
196
|
return;
|
|
146
197
|
let body;
|
|
@@ -165,6 +216,7 @@ function createMessageChannel(poster, key, instanceId, context, processorRegistr
|
|
|
165
216
|
if (!isValidMessagePayload(body))
|
|
166
217
|
return;
|
|
167
218
|
const meta = { senderInstanceId: body.senderInstanceId };
|
|
219
|
+
context.noteRemoteSeen(body.senderInstanceId);
|
|
168
220
|
try {
|
|
169
221
|
handler(deserializeMessageData(body), context, meta);
|
|
170
222
|
}
|
|
@@ -185,46 +237,47 @@ function createMessageChannel(poster, key, instanceId, context, processorRegistr
|
|
|
185
237
|
function isMessageEnvelope(value) {
|
|
186
238
|
if (!isRecord(value))
|
|
187
239
|
return false;
|
|
188
|
-
if (typeof value.type !== 'string' || value.type.length === 0)
|
|
240
|
+
if (typeof value.type !== 'string' || value.type.length === 0 || value.type.length > MAX_TYPE_LENGTH)
|
|
189
241
|
return false;
|
|
190
242
|
if (typeof value.key !== 'string')
|
|
191
243
|
return false;
|
|
192
|
-
if (typeof value.senderInstanceId !== 'string' ||
|
|
244
|
+
if (typeof value.senderInstanceId !== 'string' ||
|
|
245
|
+
value.senderInstanceId.length === 0 ||
|
|
246
|
+
value.senderInstanceId.length > MAX_ID_LENGTH)
|
|
193
247
|
return false;
|
|
194
|
-
if ('targetInstanceId' in value &&
|
|
248
|
+
if ('targetInstanceId' in value &&
|
|
249
|
+
value.targetInstanceId !== undefined &&
|
|
250
|
+
(typeof value.targetInstanceId !== 'string' || value.targetInstanceId.length > MAX_ID_LENGTH))
|
|
195
251
|
return false;
|
|
196
252
|
return true;
|
|
197
253
|
}
|
|
198
254
|
function isValidMessagePayload(value) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
case 'accessPropertyRequest':
|
|
205
|
-
return isAccessPropertyRequest(value.data);
|
|
206
|
-
case 'accessPropertyResponse':
|
|
207
|
-
return isResponse(value.data);
|
|
208
|
-
case 'invokeFunctionByIdRequest':
|
|
209
|
-
return isCallbackRequest(value.data);
|
|
210
|
-
case 'invokeFunctionByIdResponse':
|
|
211
|
-
return isResponse(value.data);
|
|
212
|
-
case 'releaseCallbacks':
|
|
213
|
-
return isReleaseCallbacks(value.data);
|
|
214
|
-
case 'destroyEndpoint':
|
|
215
|
-
return isDestroyEndpoint(value.data);
|
|
216
|
-
default:
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
255
|
+
const spec = MESSAGE_SPECS[value.type];
|
|
256
|
+
return spec ? spec.validate(value.data) : false;
|
|
257
|
+
}
|
|
258
|
+
function isConnectMessage(value) {
|
|
259
|
+
return isRecord(value) && isMessageId(value.instanceId);
|
|
219
260
|
}
|
|
220
261
|
function isInvokeRequest(value) {
|
|
221
|
-
return isRecord(value) &&
|
|
262
|
+
return (isRecord(value) &&
|
|
263
|
+
isMessageId(value.id) &&
|
|
264
|
+
isSerializedPath(value.path) &&
|
|
265
|
+
value.path.length <= MAX_PATH_LENGTH &&
|
|
266
|
+
Array.isArray(value.args) &&
|
|
267
|
+
value.args.length <= MAX_INVOKE_ARGS);
|
|
222
268
|
}
|
|
223
269
|
function isAccessPropertyRequest(value) {
|
|
224
|
-
return isRecord(value) &&
|
|
270
|
+
return (isRecord(value) &&
|
|
271
|
+
isMessageId(value.id) &&
|
|
272
|
+
isSerializedPath(value.path) &&
|
|
273
|
+
value.path.length <= MAX_PATH_LENGTH);
|
|
225
274
|
}
|
|
226
275
|
function isCallbackRequest(value) {
|
|
227
|
-
return isRecord(value) &&
|
|
276
|
+
return (isRecord(value) &&
|
|
277
|
+
isMessageId(value.id) &&
|
|
278
|
+
isMessageId(value.callId) &&
|
|
279
|
+
Array.isArray(value.args) &&
|
|
280
|
+
value.args.length <= MAX_INVOKE_ARGS);
|
|
228
281
|
}
|
|
229
282
|
function isResponse(value) {
|
|
230
283
|
return (isRecord(value) &&
|
|
@@ -233,31 +286,21 @@ function isResponse(value) {
|
|
|
233
286
|
('data' in value || 'error' in value));
|
|
234
287
|
}
|
|
235
288
|
function isReleaseCallbacks(value) {
|
|
236
|
-
return isRecord(value) && Array.isArray(value.ids) && value.ids.
|
|
289
|
+
return (isRecord(value) && Array.isArray(value.ids) && value.ids.length <= MAX_RELEASE_IDS && value.ids.every(isMessageId));
|
|
237
290
|
}
|
|
238
291
|
function isDestroyEndpoint(value) {
|
|
239
|
-
return isRecord(value) &&
|
|
292
|
+
return isRecord(value) && isMessageId(value.instanceId);
|
|
240
293
|
}
|
|
241
294
|
function isSerializedError(value) {
|
|
242
295
|
return (isRecord(value) && typeof value.message === 'string' && (!('stack' in value) || typeof value.stack === 'string'));
|
|
243
296
|
}
|
|
244
297
|
function isMessageId(value) {
|
|
245
|
-
return typeof value === 'string' && value.length > 0;
|
|
298
|
+
return typeof value === 'string' && value.length > 0 && value.length <= MAX_ID_LENGTH;
|
|
246
299
|
}
|
|
247
300
|
function deserializeMessageData(body) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return
|
|
251
|
-
...data,
|
|
252
|
-
path: deserializePath(data.path),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
if (body.type === 'accessPropertyRequest') {
|
|
256
|
-
const data = body.data;
|
|
257
|
-
return {
|
|
258
|
-
...data,
|
|
259
|
-
path: deserializePath(data.path),
|
|
260
|
-
};
|
|
301
|
+
const spec = MESSAGE_SPECS[body.type];
|
|
302
|
+
if (spec.preDeserialize) {
|
|
303
|
+
return spec.preDeserialize(body.data);
|
|
261
304
|
}
|
|
262
305
|
return body.data;
|
|
263
306
|
}
|
|
@@ -265,38 +308,39 @@ function isRecord(value) {
|
|
|
265
308
|
return typeof value === 'object' && value !== null;
|
|
266
309
|
}
|
|
267
310
|
function sendProcessorError(body, sender, err) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
sender.sendMessage('invokeResponse', {
|
|
271
|
-
id: data.id,
|
|
272
|
-
error: serializeThrownError(err),
|
|
273
|
-
}, body.senderInstanceId);
|
|
311
|
+
const spec = MESSAGE_SPECS[body.type];
|
|
312
|
+
if (!spec.errorResponse)
|
|
274
313
|
return;
|
|
275
|
-
|
|
276
|
-
if (body.type === 'accessPropertyRequest') {
|
|
277
|
-
const data = body.data;
|
|
278
|
-
sender.sendMessage('accessPropertyResponse', {
|
|
279
|
-
id: data.id,
|
|
280
|
-
error: serializeThrownError(err),
|
|
281
|
-
}, body.senderInstanceId);
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
314
|
+
const error = serializeThrownError(err);
|
|
284
315
|
if (body.type === 'invokeFunctionByIdRequest') {
|
|
285
316
|
const data = body.data;
|
|
286
|
-
const error = serializeThrownError(err);
|
|
287
317
|
warn('sendProcessorError', `callback '${data.id}' failed: ${error.message}`);
|
|
288
|
-
sender.sendMessage('invokeFunctionByIdResponse', {
|
|
289
|
-
id: data.callId,
|
|
290
|
-
error,
|
|
291
|
-
}, body.senderInstanceId);
|
|
292
318
|
}
|
|
319
|
+
sender.sendMessage(spec.errorResponse.type, { id: spec.errorResponse.extractId(body.data), error }, body.senderInstanceId);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function isListenerRegistrationPath(path) {
|
|
323
|
+
const last = path[path.length - 1];
|
|
324
|
+
return last === 'addListener';
|
|
325
|
+
}
|
|
326
|
+
function isListenerRemovalPath(path) {
|
|
327
|
+
const last = path[path.length - 1];
|
|
328
|
+
return last === 'removeListener';
|
|
329
|
+
}
|
|
330
|
+
function isLikelyListenerPath(path) {
|
|
331
|
+
const last = path[path.length - 1];
|
|
332
|
+
return typeof last === 'string' && /^on[A-Z]/.test(last);
|
|
293
333
|
}
|
|
294
334
|
|
|
295
335
|
const TYPE_KEY = '$cii$';
|
|
336
|
+
const MAX_SERIALIZE_DEPTH = 200;
|
|
296
337
|
function serialize(arg, registerFunction, options = {}) {
|
|
297
|
-
return serialize0(arg, undefined, registerFunction, options, new WeakSet());
|
|
338
|
+
return serialize0(arg, undefined, registerFunction, options, new WeakSet(), 0);
|
|
298
339
|
}
|
|
299
|
-
function serialize0(arg, owner, registerFunction, options, seen) {
|
|
340
|
+
function serialize0(arg, owner, registerFunction, options, seen, depth) {
|
|
341
|
+
if (depth > MAX_SERIALIZE_DEPTH) {
|
|
342
|
+
throw new TypeError(`Serialization exceeded maximum depth of ${MAX_SERIALIZE_DEPTH}`);
|
|
343
|
+
}
|
|
300
344
|
if (arg === undefined)
|
|
301
345
|
return { [TYPE_KEY]: 'undef' };
|
|
302
346
|
if (arg === null)
|
|
@@ -315,15 +359,22 @@ function serialize0(arg, owner, registerFunction, options, seen) {
|
|
|
315
359
|
return options.persistent ? { [TYPE_KEY]: 'fn', id, persistent: true } : { [TYPE_KEY]: 'fn', id };
|
|
316
360
|
}
|
|
317
361
|
if (typeOf === 'symbol')
|
|
318
|
-
return { [TYPE_KEY]: 'sym', v:
|
|
362
|
+
return { [TYPE_KEY]: 'sym', v: encodeSymbolToken(arg) };
|
|
319
363
|
if (typeOf === 'object') {
|
|
320
364
|
if (seen.has(arg)) {
|
|
321
365
|
throw new TypeError('Cannot serialize circular structure');
|
|
322
366
|
}
|
|
323
367
|
seen.add(arg);
|
|
324
368
|
try {
|
|
369
|
+
if (Array.isArray(arg)) {
|
|
370
|
+
return arg.map((item) => serialize0(item, arg, registerFunction, options, seen, depth + 1));
|
|
371
|
+
}
|
|
372
|
+
const proto = Object.getPrototypeOf(arg);
|
|
373
|
+
if (proto === Object.prototype || proto === null) {
|
|
374
|
+
return serializePlainObject(arg, registerFunction, options, seen, depth);
|
|
375
|
+
}
|
|
325
376
|
if (arg instanceof Error)
|
|
326
|
-
return serializeError(arg, owner, registerFunction, options, seen);
|
|
377
|
+
return serializeError(arg, owner, registerFunction, options, seen, depth);
|
|
327
378
|
if (arg instanceof Date)
|
|
328
379
|
return { [TYPE_KEY]: 'date', v: arg.getTime() };
|
|
329
380
|
if (arg instanceof RegExp)
|
|
@@ -332,8 +383,8 @@ function serialize0(arg, owner, registerFunction, options, seen) {
|
|
|
332
383
|
const entries = [];
|
|
333
384
|
for (const [k, v] of arg.entries()) {
|
|
334
385
|
entries.push([
|
|
335
|
-
serialize0(k, arg, registerFunction, options, seen),
|
|
336
|
-
serialize0(v, arg, registerFunction, options, seen),
|
|
386
|
+
serialize0(k, arg, registerFunction, options, seen, depth + 1),
|
|
387
|
+
serialize0(v, arg, registerFunction, options, seen, depth + 1),
|
|
337
388
|
]);
|
|
338
389
|
}
|
|
339
390
|
return { [TYPE_KEY]: 'map', entries };
|
|
@@ -341,14 +392,11 @@ function serialize0(arg, owner, registerFunction, options, seen) {
|
|
|
341
392
|
if (arg instanceof Set) {
|
|
342
393
|
const values = [];
|
|
343
394
|
for (const v of arg.values()) {
|
|
344
|
-
values.push(serialize0(v, arg, registerFunction, options, seen));
|
|
395
|
+
values.push(serialize0(v, arg, registerFunction, options, seen, depth + 1));
|
|
345
396
|
}
|
|
346
397
|
return { [TYPE_KEY]: 'set', values };
|
|
347
398
|
}
|
|
348
|
-
|
|
349
|
-
return arg.map((item) => serialize0(item, arg, registerFunction, options, seen));
|
|
350
|
-
}
|
|
351
|
-
return serializePlainObject(arg, registerFunction, options, seen);
|
|
399
|
+
return serializePlainObject(arg, registerFunction, options, seen, depth);
|
|
352
400
|
}
|
|
353
401
|
finally {
|
|
354
402
|
seen.delete(arg);
|
|
@@ -356,7 +404,7 @@ function serialize0(arg, owner, registerFunction, options, seen) {
|
|
|
356
404
|
}
|
|
357
405
|
return null;
|
|
358
406
|
}
|
|
359
|
-
function serializeError(err, owner, registerFunction, options, seen) {
|
|
407
|
+
function serializeError(err, owner, registerFunction, options, seen, depth) {
|
|
360
408
|
const result = { [TYPE_KEY]: 'err', message: err.message };
|
|
361
409
|
if (err.name && err.name !== 'Error')
|
|
362
410
|
result.name = err.name;
|
|
@@ -364,7 +412,7 @@ function serializeError(err, owner, registerFunction, options, seen) {
|
|
|
364
412
|
result.stack = err.stack;
|
|
365
413
|
const cause = err.cause;
|
|
366
414
|
if (cause !== undefined) {
|
|
367
|
-
result.cause = serialize0(cause, owner, registerFunction, options, seen);
|
|
415
|
+
result.cause = serialize0(cause, owner, registerFunction, options, seen, depth + 1);
|
|
368
416
|
}
|
|
369
417
|
const RESERVED_ERROR_KEYS = new Set(['message', 'name', 'stack', 'cause']);
|
|
370
418
|
const extras = [];
|
|
@@ -373,14 +421,16 @@ function serializeError(err, owner, registerFunction, options, seen) {
|
|
|
373
421
|
continue;
|
|
374
422
|
if (RESERVED_ERROR_KEYS.has(k))
|
|
375
423
|
continue;
|
|
424
|
+
if (!isEnumerable(err, k))
|
|
425
|
+
continue;
|
|
376
426
|
const value = err[k];
|
|
377
|
-
extras.push([k, serialize0(value, owner, registerFunction, options, seen)]);
|
|
427
|
+
extras.push([k, serialize0(value, owner, registerFunction, options, seen, depth + 1)]);
|
|
378
428
|
}
|
|
379
429
|
if (extras.length > 0)
|
|
380
430
|
result.extras = extras;
|
|
381
431
|
return result;
|
|
382
432
|
}
|
|
383
|
-
function serializePlainObject(source, registerFunction, options, seen) {
|
|
433
|
+
function serializePlainObject(source, registerFunction, options, seen, depth) {
|
|
384
434
|
const record = source;
|
|
385
435
|
const ownKeys = Reflect.ownKeys(source).filter((k) => isEnumerable(source, k));
|
|
386
436
|
const symbolKeys = [];
|
|
@@ -398,19 +448,25 @@ function serializePlainObject(source, registerFunction, options, seen) {
|
|
|
398
448
|
if (symbolKeys.length > 0 || hasReservedKey) {
|
|
399
449
|
const entries = [];
|
|
400
450
|
for (const k of stringKeys) {
|
|
401
|
-
entries.push([k, serialize0(record[k], source, registerFunction, options, seen)]);
|
|
451
|
+
entries.push([k, serialize0(record[k], source, registerFunction, options, seen, depth + 1)]);
|
|
402
452
|
}
|
|
403
453
|
for (const k of symbolKeys) {
|
|
404
454
|
entries.push([
|
|
405
|
-
{ [TYPE_KEY]: 'sym', v:
|
|
406
|
-
serialize0(record[k], source, registerFunction, options, seen),
|
|
455
|
+
{ [TYPE_KEY]: 'sym', v: encodeSymbolToken(k) },
|
|
456
|
+
serialize0(record[k], source, registerFunction, options, seen, depth + 1),
|
|
407
457
|
]);
|
|
408
458
|
}
|
|
409
459
|
return { [TYPE_KEY]: 'obj', entries };
|
|
410
460
|
}
|
|
411
461
|
const result = {};
|
|
412
462
|
for (const k of stringKeys) {
|
|
413
|
-
|
|
463
|
+
const value = serialize0(record[k], source, registerFunction, options, seen, depth + 1);
|
|
464
|
+
if (k === '__proto__') {
|
|
465
|
+
setOwnProperty(result, k, value);
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
result[k] = value;
|
|
469
|
+
}
|
|
414
470
|
}
|
|
415
471
|
return result;
|
|
416
472
|
}
|
|
@@ -434,56 +490,61 @@ function serializeNumber(value) {
|
|
|
434
490
|
return { [TYPE_KEY]: 'num', v: '-0' };
|
|
435
491
|
return value;
|
|
436
492
|
}
|
|
437
|
-
function setOwnProperty$1(target, key, value) {
|
|
438
|
-
Object.defineProperty(target, key, {
|
|
439
|
-
value,
|
|
440
|
-
enumerable: true,
|
|
441
|
-
configurable: true,
|
|
442
|
-
writable: true,
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
function serializeSymbol(value) {
|
|
446
|
-
const globalKey = Symbol.keyFor(value);
|
|
447
|
-
if (globalKey)
|
|
448
|
-
return `global:${globalKey}`;
|
|
449
|
-
const wellKnownName = getWellKnownSymbolName(value);
|
|
450
|
-
if (wellKnownName)
|
|
451
|
-
return `wellKnown:${wellKnownName}`;
|
|
452
|
-
throw new TypeError('Cannot serialize non-global symbol');
|
|
453
|
-
}
|
|
454
493
|
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
494
|
+
const MAX_DESERIALIZE_DEPTH = 200;
|
|
495
|
+
const ERROR_FACTORIES = {
|
|
496
|
+
Error: (m) => new Error(m),
|
|
497
|
+
TypeError: (m) => new TypeError(m),
|
|
498
|
+
RangeError: (m) => new RangeError(m),
|
|
499
|
+
SyntaxError: (m) => new SyntaxError(m),
|
|
500
|
+
ReferenceError: (m) => new ReferenceError(m),
|
|
501
|
+
URIError: (m) => new URIError(m),
|
|
502
|
+
EvalError: (m) => new EvalError(m),
|
|
463
503
|
};
|
|
504
|
+
const globalScope = globalThis;
|
|
505
|
+
const AggregateErrorCtorRef = globalScope.AggregateError;
|
|
506
|
+
if (typeof AggregateErrorCtorRef === 'function') {
|
|
507
|
+
ERROR_FACTORIES.AggregateError = (m) => new AggregateErrorCtorRef([], m);
|
|
508
|
+
}
|
|
509
|
+
const DOMExceptionCtorRef = globalScope.DOMException;
|
|
510
|
+
if (typeof DOMExceptionCtorRef === 'function') {
|
|
511
|
+
ERROR_FACTORIES.DOMException = (m) => new DOMExceptionCtorRef(m);
|
|
512
|
+
}
|
|
464
513
|
function deserialize(arg, generateCallback, getRemoteCallback) {
|
|
465
|
-
return deserialize0(arg, generateCallback, getRemoteCallback);
|
|
514
|
+
return deserialize0(arg, generateCallback, getRemoteCallback, 0);
|
|
466
515
|
}
|
|
467
|
-
function deserialize0(arg, generateCallback, getRemoteCallback) {
|
|
516
|
+
function deserialize0(arg, generateCallback, getRemoteCallback, depth) {
|
|
517
|
+
if (depth > MAX_DESERIALIZE_DEPTH) {
|
|
518
|
+
throw new TypeError(`Deserialization exceeded maximum depth of ${MAX_DESERIALIZE_DEPTH}`);
|
|
519
|
+
}
|
|
468
520
|
if (arg === null)
|
|
469
521
|
return null;
|
|
470
522
|
if (typeof arg !== 'object')
|
|
471
523
|
return arg;
|
|
472
524
|
if (Array.isArray(arg)) {
|
|
473
|
-
return arg.map((item) => deserialize0(item, generateCallback, getRemoteCallback));
|
|
525
|
+
return arg.map((item) => deserialize0(item, generateCallback, getRemoteCallback, depth + 1));
|
|
474
526
|
}
|
|
475
527
|
const source = arg;
|
|
476
528
|
const tag = source[TYPE_KEY];
|
|
477
529
|
if (typeof tag === 'string') {
|
|
478
|
-
return deserializeTagged(tag, source, generateCallback, getRemoteCallback);
|
|
530
|
+
return deserializeTagged(tag, source, generateCallback, getRemoteCallback, depth);
|
|
479
531
|
}
|
|
532
|
+
return deserializePlainObject(source, generateCallback, getRemoteCallback, depth);
|
|
533
|
+
}
|
|
534
|
+
function deserializePlainObject(source, generateCallback, getRemoteCallback, depth) {
|
|
480
535
|
const result = {};
|
|
481
536
|
for (const key of Object.keys(source)) {
|
|
482
|
-
|
|
537
|
+
const value = deserialize0(source[key], generateCallback, getRemoteCallback, depth + 1);
|
|
538
|
+
if (key === '__proto__') {
|
|
539
|
+
setOwnProperty(result, key, value);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
result[key] = value;
|
|
543
|
+
}
|
|
483
544
|
}
|
|
484
545
|
return result;
|
|
485
546
|
}
|
|
486
|
-
function deserializeTagged(tag, source, generateCallback, getRemoteCallback) {
|
|
547
|
+
function deserializeTagged(tag, source, generateCallback, getRemoteCallback, depth) {
|
|
487
548
|
switch (tag) {
|
|
488
549
|
case 'undef':
|
|
489
550
|
return undefined;
|
|
@@ -494,26 +555,21 @@ function deserializeTagged(tag, source, generateCallback, getRemoteCallback) {
|
|
|
494
555
|
case 'num':
|
|
495
556
|
return deserializeNumber(expectString(source.v, 'number'));
|
|
496
557
|
case 'err':
|
|
497
|
-
return deserializeError(source, generateCallback, getRemoteCallback);
|
|
558
|
+
return deserializeError(source, generateCallback, getRemoteCallback, depth);
|
|
498
559
|
case 'date':
|
|
499
560
|
return deserializeDate(source.v);
|
|
500
561
|
case 're':
|
|
501
562
|
return deserializeRegExp(source.source, source.flags);
|
|
502
563
|
case 'map':
|
|
503
|
-
return deserializeMap(source.entries, generateCallback, getRemoteCallback);
|
|
564
|
+
return deserializeMap(source.entries, generateCallback, getRemoteCallback, depth);
|
|
504
565
|
case 'set':
|
|
505
|
-
return deserializeSet(source.values, generateCallback, getRemoteCallback);
|
|
566
|
+
return deserializeSet(source.values, generateCallback, getRemoteCallback, depth);
|
|
506
567
|
case 'fn':
|
|
507
568
|
return deserializeFunction(source, generateCallback, getRemoteCallback);
|
|
508
569
|
case 'obj':
|
|
509
|
-
return deserializeWrappedObject(source.entries, generateCallback, getRemoteCallback);
|
|
510
|
-
default:
|
|
511
|
-
|
|
512
|
-
for (const key of Object.keys(source)) {
|
|
513
|
-
setOwnProperty(result, key, deserialize0(source[key], generateCallback, getRemoteCallback));
|
|
514
|
-
}
|
|
515
|
-
return result;
|
|
516
|
-
}
|
|
570
|
+
return deserializeWrappedObject(source.entries, generateCallback, getRemoteCallback, depth);
|
|
571
|
+
default:
|
|
572
|
+
return deserializePlainObject(source, generateCallback, getRemoteCallback, depth);
|
|
517
573
|
}
|
|
518
574
|
}
|
|
519
575
|
function deserializeBigInt(raw) {
|
|
@@ -544,25 +600,25 @@ function deserializeRegExp(rawSource, rawFlags) {
|
|
|
544
600
|
throw new TypeError(`Invalid regexp payload: ${err instanceof Error ? err.message : String(err)}`);
|
|
545
601
|
}
|
|
546
602
|
}
|
|
547
|
-
function deserializeMap(entries, generateCallback, getRemoteCallback) {
|
|
603
|
+
function deserializeMap(entries, generateCallback, getRemoteCallback, depth) {
|
|
548
604
|
const result = new Map();
|
|
549
605
|
if (!Array.isArray(entries))
|
|
550
606
|
return result;
|
|
551
607
|
for (const entry of entries) {
|
|
552
608
|
if (!Array.isArray(entry) || entry.length !== 2)
|
|
553
609
|
continue;
|
|
554
|
-
const k = deserialize0(entry[0], generateCallback, getRemoteCallback);
|
|
555
|
-
const v = deserialize0(entry[1], generateCallback, getRemoteCallback);
|
|
610
|
+
const k = deserialize0(entry[0], generateCallback, getRemoteCallback, depth + 1);
|
|
611
|
+
const v = deserialize0(entry[1], generateCallback, getRemoteCallback, depth + 1);
|
|
556
612
|
result.set(k, v);
|
|
557
613
|
}
|
|
558
614
|
return result;
|
|
559
615
|
}
|
|
560
|
-
function deserializeSet(values, generateCallback, getRemoteCallback) {
|
|
616
|
+
function deserializeSet(values, generateCallback, getRemoteCallback, depth) {
|
|
561
617
|
const result = new Set();
|
|
562
618
|
if (!Array.isArray(values))
|
|
563
619
|
return result;
|
|
564
620
|
for (const v of values) {
|
|
565
|
-
result.add(deserialize0(v, generateCallback, getRemoteCallback));
|
|
621
|
+
result.add(deserialize0(v, generateCallback, getRemoteCallback, depth + 1));
|
|
566
622
|
}
|
|
567
623
|
return result;
|
|
568
624
|
}
|
|
@@ -580,23 +636,35 @@ function deserializeFunction(source, generateCallback, getRemoteCallback) {
|
|
|
580
636
|
};
|
|
581
637
|
return getRemoteCallback ? getRemoteCallback(id, invoke, fnOptions) : (...args) => invoke(args);
|
|
582
638
|
}
|
|
583
|
-
function
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
if (name && name !== Ctor.name) {
|
|
639
|
+
function rebuildErrorFromSerialized(serialized) {
|
|
640
|
+
const name = typeof serialized.name === 'string' && serialized.name ? serialized.name : 'Error';
|
|
641
|
+
const factory = ERROR_FACTORIES[name] ?? ERROR_FACTORIES.Error;
|
|
642
|
+
const error = factory(serialized.message);
|
|
643
|
+
if (name && error.name !== name) {
|
|
589
644
|
try {
|
|
590
645
|
error.name = name;
|
|
591
646
|
}
|
|
592
|
-
catch
|
|
593
|
-
|
|
647
|
+
catch {
|
|
648
|
+
try {
|
|
649
|
+
Object.defineProperty(error, 'name', { value: name, configurable: true, writable: true });
|
|
650
|
+
}
|
|
651
|
+
catch (err) {
|
|
652
|
+
warn('rebuildErrorFromSerialized', 'failed to set error.name', name, err);
|
|
653
|
+
}
|
|
594
654
|
}
|
|
595
655
|
}
|
|
596
|
-
if (typeof
|
|
597
|
-
error.stack =
|
|
656
|
+
if (typeof serialized.stack === 'string')
|
|
657
|
+
error.stack = serialized.stack;
|
|
658
|
+
return error;
|
|
659
|
+
}
|
|
660
|
+
function deserializeError(source, generateCallback, getRemoteCallback, depth) {
|
|
661
|
+
const error = rebuildErrorFromSerialized({
|
|
662
|
+
message: typeof source.message === 'string' ? source.message : '',
|
|
663
|
+
name: typeof source.name === 'string' ? source.name : undefined,
|
|
664
|
+
stack: typeof source.stack === 'string' ? source.stack : undefined,
|
|
665
|
+
});
|
|
598
666
|
if (source.cause !== undefined) {
|
|
599
|
-
error.cause = deserialize0(source.cause, generateCallback, getRemoteCallback);
|
|
667
|
+
error.cause = deserialize0(source.cause, generateCallback, getRemoteCallback, depth + 1);
|
|
600
668
|
}
|
|
601
669
|
if (Array.isArray(source.extras)) {
|
|
602
670
|
for (const entry of source.extras) {
|
|
@@ -606,7 +674,7 @@ function deserializeError(source, generateCallback, getRemoteCallback) {
|
|
|
606
674
|
if (typeof rawKey !== 'string')
|
|
607
675
|
continue;
|
|
608
676
|
try {
|
|
609
|
-
setOwnProperty(error, rawKey, deserialize0(rawValue, generateCallback, getRemoteCallback));
|
|
677
|
+
setOwnProperty(error, rawKey, deserialize0(rawValue, generateCallback, getRemoteCallback, depth + 1));
|
|
610
678
|
}
|
|
611
679
|
catch (err) {
|
|
612
680
|
warn('deserializeError', 'failed to set error property', rawKey, err);
|
|
@@ -615,7 +683,7 @@ function deserializeError(source, generateCallback, getRemoteCallback) {
|
|
|
615
683
|
}
|
|
616
684
|
return error;
|
|
617
685
|
}
|
|
618
|
-
function deserializeWrappedObject(entries, generateCallback, getRemoteCallback) {
|
|
686
|
+
function deserializeWrappedObject(entries, generateCallback, getRemoteCallback, depth) {
|
|
619
687
|
const result = {};
|
|
620
688
|
if (!Array.isArray(entries))
|
|
621
689
|
return result;
|
|
@@ -623,18 +691,13 @@ function deserializeWrappedObject(entries, generateCallback, getRemoteCallback)
|
|
|
623
691
|
if (!Array.isArray(entry) || entry.length !== 2)
|
|
624
692
|
continue;
|
|
625
693
|
const rawKey = entry[0];
|
|
626
|
-
const value = deserialize0(entry[1], generateCallback, getRemoteCallback);
|
|
694
|
+
const value = deserialize0(entry[1], generateCallback, getRemoteCallback, depth + 1);
|
|
627
695
|
const key = resolveObjectKey(rawKey);
|
|
628
696
|
if (key === undefined) {
|
|
629
697
|
warn('deserializeWrappedObject', 'dropping wrapped-object entry with unresolvable key', rawKey);
|
|
630
698
|
continue;
|
|
631
699
|
}
|
|
632
|
-
|
|
633
|
-
value,
|
|
634
|
-
enumerable: true,
|
|
635
|
-
configurable: true,
|
|
636
|
-
writable: true,
|
|
637
|
-
});
|
|
700
|
+
setOwnProperty(result, key, value);
|
|
638
701
|
}
|
|
639
702
|
return result;
|
|
640
703
|
}
|
|
@@ -672,39 +735,88 @@ function deserializeNumber(raw) {
|
|
|
672
735
|
return Number(raw);
|
|
673
736
|
}
|
|
674
737
|
function deserializeSymbol(value) {
|
|
675
|
-
|
|
676
|
-
return Symbol.for(value.slice('global:'.length));
|
|
677
|
-
}
|
|
678
|
-
if (value.startsWith('wellKnown:')) {
|
|
679
|
-
const symbol = getWellKnownSymbol(value.slice('wellKnown:'.length));
|
|
680
|
-
if (symbol)
|
|
681
|
-
return symbol;
|
|
682
|
-
}
|
|
683
|
-
throw new TypeError('Cannot deserialize symbol');
|
|
684
|
-
}
|
|
685
|
-
function setOwnProperty(target, key, value) {
|
|
686
|
-
Object.defineProperty(target, key, {
|
|
687
|
-
value,
|
|
688
|
-
enumerable: true,
|
|
689
|
-
configurable: true,
|
|
690
|
-
writable: true,
|
|
691
|
-
});
|
|
738
|
+
return decodeSymbolToken(value);
|
|
692
739
|
}
|
|
693
740
|
|
|
694
|
-
function
|
|
695
|
-
const
|
|
696
|
-
|
|
741
|
+
function createResponder(ctx, targetInstanceId, responseType, id) {
|
|
742
|
+
const sender = () => ctx.getMessageChannel().getSender();
|
|
743
|
+
const sendError = (error) => {
|
|
744
|
+
sender().sendMessage(responseType, { id, error }, targetInstanceId);
|
|
745
|
+
};
|
|
746
|
+
return {
|
|
747
|
+
respondError(message) {
|
|
748
|
+
sendError({ message });
|
|
749
|
+
},
|
|
750
|
+
respondThrown(err) {
|
|
751
|
+
sendError(serializeThrownError(err));
|
|
752
|
+
},
|
|
753
|
+
respondSuccess(value) {
|
|
754
|
+
try {
|
|
755
|
+
sender().sendMessage(responseType, { id, data: ctx.serializeForRemote(value, targetInstanceId) }, targetInstanceId);
|
|
756
|
+
}
|
|
757
|
+
catch (err) {
|
|
758
|
+
sendError(serializeThrownError(err));
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
};
|
|
697
762
|
}
|
|
698
|
-
function
|
|
699
|
-
|
|
700
|
-
return last === 'removeListener';
|
|
763
|
+
function createScopedRemoteCallback(ctx, remoteInstanceId) {
|
|
764
|
+
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
701
765
|
}
|
|
702
|
-
function
|
|
703
|
-
|
|
704
|
-
|
|
766
|
+
function createGenerateCallback(ctx, remoteInstanceId) {
|
|
767
|
+
return (id, args, options) => ctx.invokeFunctionById(id, args, options, remoteInstanceId);
|
|
768
|
+
}
|
|
769
|
+
function safeInvokeReject(callbacks, scope, warn) {
|
|
770
|
+
if (!callbacks.onReject)
|
|
771
|
+
return;
|
|
772
|
+
try {
|
|
773
|
+
callbacks.onReject();
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
warn(scope, 'onReject hook threw', err);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function safeInvokeResolve(callbacks, scope, warn) {
|
|
780
|
+
if (!callbacks.onResolve)
|
|
781
|
+
return;
|
|
782
|
+
try {
|
|
783
|
+
callbacks.onResolve();
|
|
784
|
+
}
|
|
785
|
+
catch (err) {
|
|
786
|
+
warn(scope, 'onResolve hook threw', err);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function createResponseHandler(scope, warn) {
|
|
790
|
+
return function handleResponse(data, ctx, meta) {
|
|
791
|
+
const pending = ctx.getAndRemovePendingPromise(data.id, meta.senderInstanceId);
|
|
792
|
+
if (!pending)
|
|
793
|
+
return;
|
|
794
|
+
if (data.error) {
|
|
795
|
+
const err = rebuildErrorFromSerialized(data.error);
|
|
796
|
+
safeInvokeReject(pending, scope, warn);
|
|
797
|
+
pending.reject(err);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
try {
|
|
801
|
+
safeInvokeResolve(pending, scope, warn);
|
|
802
|
+
pending.resolve(deserialize(data.data, createGenerateCallback(ctx, meta.senderInstanceId), createScopedRemoteCallback(ctx, meta.senderInstanceId)));
|
|
803
|
+
}
|
|
804
|
+
catch (err) {
|
|
805
|
+
safeInvokeReject(pending, scope, warn);
|
|
806
|
+
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
807
|
+
}
|
|
808
|
+
};
|
|
705
809
|
}
|
|
706
810
|
|
|
707
|
-
const DANGEROUS_KEYS = new Set([
|
|
811
|
+
const DANGEROUS_KEYS = new Set([
|
|
812
|
+
'__proto__',
|
|
813
|
+
'constructor',
|
|
814
|
+
'prototype',
|
|
815
|
+
'__defineGetter__',
|
|
816
|
+
'__defineSetter__',
|
|
817
|
+
'__lookupGetter__',
|
|
818
|
+
'__lookupSetter__',
|
|
819
|
+
]);
|
|
708
820
|
function readProperty(target, key) {
|
|
709
821
|
if (typeof key === 'string' && DANGEROUS_KEYS.has(key)) {
|
|
710
822
|
throw new Error(`Access to dangerous property '${key}' is denied`);
|
|
@@ -715,20 +827,16 @@ function readProperty(target, key) {
|
|
|
715
827
|
}
|
|
716
828
|
|
|
717
829
|
function handleAccessPropertyRequest(data, ctx, meta) {
|
|
830
|
+
if (ctx.isDestroyed())
|
|
831
|
+
return;
|
|
718
832
|
const target = ctx.getDelegateTarget();
|
|
719
|
-
const
|
|
833
|
+
const { respondError, respondThrown } = createResponder(ctx, meta.senderInstanceId, 'accessPropertyResponse', data.id);
|
|
720
834
|
if (data.path.length === 0) {
|
|
721
|
-
|
|
722
|
-
id: data.id,
|
|
723
|
-
error: { message: 'Property path must not be empty' },
|
|
724
|
-
}, meta.senderInstanceId);
|
|
835
|
+
respondError('Property path must not be empty');
|
|
725
836
|
return;
|
|
726
837
|
}
|
|
727
838
|
if (target === undefined || target === null) {
|
|
728
|
-
|
|
729
|
-
id: data.id,
|
|
730
|
-
error: { message: 'No delegate target is configured' },
|
|
731
|
-
}, meta.senderInstanceId);
|
|
839
|
+
respondError('No delegate target is configured');
|
|
732
840
|
return;
|
|
733
841
|
}
|
|
734
842
|
try {
|
|
@@ -737,115 +845,70 @@ function handleAccessPropertyRequest(data, ctx, meta) {
|
|
|
737
845
|
for (let i = 0; i < data.path.length; i++) {
|
|
738
846
|
const key = data.path[i];
|
|
739
847
|
if (current === undefined || current === null) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
.slice(0, i)
|
|
745
|
-
.map(String)
|
|
746
|
-
.join('.')}')`,
|
|
747
|
-
},
|
|
748
|
-
}, meta.senderInstanceId);
|
|
848
|
+
respondError(`Cannot read property '${String(key)}' of ${current === null ? 'null' : 'undefined'} (at '${data.path
|
|
849
|
+
.slice(0, i)
|
|
850
|
+
.map(String)
|
|
851
|
+
.join('.')}')`);
|
|
749
852
|
return;
|
|
750
853
|
}
|
|
751
854
|
owner = current;
|
|
752
855
|
current = readProperty(current, key);
|
|
753
856
|
}
|
|
754
857
|
const value = typeof current === 'function' ? ctx.bindMethod(current, owner) : current;
|
|
755
|
-
|
|
858
|
+
ctx
|
|
859
|
+
.getMessageChannel()
|
|
860
|
+
.getSender()
|
|
861
|
+
.sendMessage('accessPropertyResponse', {
|
|
756
862
|
id: data.id,
|
|
757
|
-
data:
|
|
863
|
+
data: ctx.serializeForRemote(value, meta.senderInstanceId, {
|
|
864
|
+
persistent: isLikelyListenerPath(data.path),
|
|
865
|
+
}),
|
|
758
866
|
}, meta.senderInstanceId);
|
|
759
867
|
}
|
|
760
868
|
catch (err) {
|
|
761
|
-
|
|
762
|
-
id: data.id,
|
|
763
|
-
error: serializeThrownError(err),
|
|
764
|
-
}, meta.senderInstanceId);
|
|
869
|
+
respondThrown(err);
|
|
765
870
|
}
|
|
766
871
|
}
|
|
767
|
-
|
|
768
|
-
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
769
|
-
if (!pending)
|
|
770
|
-
return;
|
|
771
|
-
if (data.error) {
|
|
772
|
-
const err = new Error(data.error.message);
|
|
773
|
-
if (data.error.stack)
|
|
774
|
-
err.stack = data.error.stack;
|
|
775
|
-
pending.onReject?.();
|
|
776
|
-
pending.reject(err);
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
const scopedRemoteCallback = createScopedRemoteCallback$2(ctx, meta.senderInstanceId);
|
|
780
|
-
try {
|
|
781
|
-
pending.onResolve?.();
|
|
782
|
-
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
783
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
784
|
-
}, scopedRemoteCallback));
|
|
785
|
-
}
|
|
786
|
-
catch (err) {
|
|
787
|
-
pending.onReject?.();
|
|
788
|
-
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
function createScopedRemoteCallback$2(ctx, remoteInstanceId) {
|
|
792
|
-
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
793
|
-
}
|
|
872
|
+
const handleAccessPropertyResponse = createResponseHandler('handleAccessPropertyResponse', warn);
|
|
794
873
|
|
|
795
874
|
function handleInvokeRequest(data, ctx, meta) {
|
|
875
|
+
if (ctx.isDestroyed())
|
|
876
|
+
return;
|
|
796
877
|
const target = ctx.getDelegateTarget();
|
|
797
|
-
const
|
|
798
|
-
const
|
|
878
|
+
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
879
|
+
const generateCallback = createGenerateCallback(ctx, meta.senderInstanceId);
|
|
880
|
+
const { respondError, respondThrown, respondSuccess } = createResponder(ctx, meta.senderInstanceId, 'invokeResponse', data.id);
|
|
799
881
|
if (target === undefined || target === null) {
|
|
800
|
-
|
|
801
|
-
id: data.id,
|
|
802
|
-
error: { message: 'No delegate target is configured' },
|
|
803
|
-
}, meta.senderInstanceId);
|
|
882
|
+
respondError('No delegate target is configured');
|
|
804
883
|
return;
|
|
805
884
|
}
|
|
806
885
|
if (data.path.length === 0) {
|
|
807
|
-
|
|
808
|
-
id: data.id,
|
|
809
|
-
error: { message: 'Invocation path must not be empty' },
|
|
810
|
-
}, meta.senderInstanceId);
|
|
886
|
+
respondError('Invocation path must not be empty');
|
|
811
887
|
return;
|
|
812
888
|
}
|
|
813
889
|
let current = target;
|
|
814
890
|
for (let i = 0; i < data.path.length - 1; i++) {
|
|
815
891
|
current = readProperty(current, data.path[i]);
|
|
816
892
|
if (current === undefined || current === null) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
.slice(0, i + 1)
|
|
822
|
-
.map(String)
|
|
823
|
-
.join('.')}')`,
|
|
824
|
-
},
|
|
825
|
-
}, meta.senderInstanceId);
|
|
893
|
+
respondError(`Cannot read property '${String(data.path[i + 1])}' of ${current === null ? 'null' : 'undefined'} (at '${data.path
|
|
894
|
+
.slice(0, i + 1)
|
|
895
|
+
.map(String)
|
|
896
|
+
.join('.')}')`);
|
|
826
897
|
return;
|
|
827
898
|
}
|
|
828
899
|
}
|
|
829
900
|
const lastKey = data.path[data.path.length - 1];
|
|
830
901
|
const fn = readProperty(current, lastKey);
|
|
831
902
|
if (typeof fn !== 'function') {
|
|
832
|
-
|
|
833
|
-
id: data.id,
|
|
834
|
-
error: { message: `'${String(lastKey)}' is not a function` },
|
|
835
|
-
}, meta.senderInstanceId);
|
|
903
|
+
respondError(`'${String(lastKey)}' is not a function`);
|
|
836
904
|
return;
|
|
837
905
|
}
|
|
838
906
|
let deserializedArgs;
|
|
839
907
|
try {
|
|
840
|
-
deserializedArgs = data.args.map((arg) => deserialize(arg,
|
|
841
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
842
|
-
}, scopedRemoteCallback));
|
|
908
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, generateCallback, scopedRemoteCallback));
|
|
843
909
|
}
|
|
844
910
|
catch (err) {
|
|
845
|
-
|
|
846
|
-
id: data.id,
|
|
847
|
-
error: serializeThrownError(err),
|
|
848
|
-
}, meta.senderInstanceId);
|
|
911
|
+
respondThrown(err);
|
|
849
912
|
return;
|
|
850
913
|
}
|
|
851
914
|
let result;
|
|
@@ -853,93 +916,43 @@ function handleInvokeRequest(data, ctx, meta) {
|
|
|
853
916
|
result = Reflect.apply(fn, current, deserializedArgs);
|
|
854
917
|
}
|
|
855
918
|
catch (err) {
|
|
856
|
-
|
|
857
|
-
id: data.id,
|
|
858
|
-
error: serializeThrownError(err),
|
|
859
|
-
}, meta.senderInstanceId);
|
|
919
|
+
respondThrown(err);
|
|
860
920
|
return;
|
|
861
921
|
}
|
|
862
922
|
if (isPromiseLike(result)) {
|
|
863
923
|
Promise.resolve(result)
|
|
864
924
|
.then((value) => {
|
|
865
|
-
|
|
925
|
+
respondSuccess(value);
|
|
866
926
|
})
|
|
867
927
|
.catch((err) => {
|
|
868
|
-
|
|
869
|
-
id: data.id,
|
|
870
|
-
error: serializeThrownError(err),
|
|
871
|
-
}, meta.senderInstanceId);
|
|
928
|
+
respondThrown(err);
|
|
872
929
|
});
|
|
873
930
|
return;
|
|
874
931
|
}
|
|
875
|
-
|
|
876
|
-
}
|
|
877
|
-
function handleInvokeResponse(data, ctx, meta) {
|
|
878
|
-
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
879
|
-
if (!pending)
|
|
880
|
-
return;
|
|
881
|
-
if (data.error) {
|
|
882
|
-
const err = new Error(data.error.message);
|
|
883
|
-
if (data.error.stack)
|
|
884
|
-
err.stack = data.error.stack;
|
|
885
|
-
pending.onReject?.();
|
|
886
|
-
pending.reject(err);
|
|
887
|
-
}
|
|
888
|
-
else {
|
|
889
|
-
const scopedRemoteCallback = createScopedRemoteCallback$1(ctx, meta.senderInstanceId);
|
|
890
|
-
try {
|
|
891
|
-
pending.onResolve?.();
|
|
892
|
-
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
893
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
894
|
-
}, scopedRemoteCallback));
|
|
895
|
-
}
|
|
896
|
-
catch (err) {
|
|
897
|
-
pending.onReject?.();
|
|
898
|
-
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
function sendInvokeSuccess(id, value, ctx, targetInstanceId) {
|
|
903
|
-
const channel = ctx.getMessageChannel();
|
|
904
|
-
try {
|
|
905
|
-
channel.getSender().sendMessage('invokeResponse', {
|
|
906
|
-
id,
|
|
907
|
-
data: serialize(value, ctx.registerFunction),
|
|
908
|
-
}, targetInstanceId);
|
|
909
|
-
}
|
|
910
|
-
catch (err) {
|
|
911
|
-
channel.getSender().sendMessage('invokeResponse', {
|
|
912
|
-
id,
|
|
913
|
-
error: serializeThrownError(err),
|
|
914
|
-
}, targetInstanceId);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
function createScopedRemoteCallback$1(ctx, remoteInstanceId) {
|
|
918
|
-
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
932
|
+
respondSuccess(result);
|
|
919
933
|
}
|
|
934
|
+
const handleInvokeResponse = createResponseHandler('handleInvokeResponse', warn);
|
|
920
935
|
|
|
921
936
|
function handleCallbackInvoke(data, ctx, meta) {
|
|
922
|
-
|
|
937
|
+
if (ctx.isDestroyed())
|
|
938
|
+
return;
|
|
939
|
+
const { respondError, respondThrown, respondSuccess } = createResponder(ctx, meta.senderInstanceId, 'invokeFunctionByIdResponse', data.callId);
|
|
923
940
|
const persistentEntry = ctx.getPersistentFunctionCache().get(data.id);
|
|
924
941
|
const entry = persistentEntry ?? ctx.getFunctionCache().get(data.id);
|
|
925
942
|
if (!entry) {
|
|
926
|
-
const
|
|
927
|
-
warn('handleCallbackInvoke',
|
|
928
|
-
|
|
929
|
-
id: data.callId,
|
|
930
|
-
error,
|
|
931
|
-
}, meta.senderInstanceId);
|
|
943
|
+
const message = `Callback '${data.id}' is not available or has expired`;
|
|
944
|
+
warn('handleCallbackInvoke', message);
|
|
945
|
+
respondError(message);
|
|
932
946
|
return;
|
|
933
947
|
}
|
|
934
948
|
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
949
|
+
const generateCallback = createGenerateCallback(ctx, meta.senderInstanceId);
|
|
935
950
|
let deserializedArgs;
|
|
936
951
|
try {
|
|
937
|
-
deserializedArgs = data.args.map((arg) => deserialize(arg,
|
|
938
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
939
|
-
}, scopedRemoteCallback));
|
|
952
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, generateCallback, scopedRemoteCallback));
|
|
940
953
|
}
|
|
941
954
|
catch (err) {
|
|
942
|
-
|
|
955
|
+
respondThrown(err);
|
|
943
956
|
return;
|
|
944
957
|
}
|
|
945
958
|
try {
|
|
@@ -947,82 +960,54 @@ function handleCallbackInvoke(data, ctx, meta) {
|
|
|
947
960
|
if (isPromiseLike(result)) {
|
|
948
961
|
Promise.resolve(result)
|
|
949
962
|
.then((value) => {
|
|
950
|
-
|
|
963
|
+
respondSuccess(value);
|
|
951
964
|
})
|
|
952
965
|
.catch((err) => {
|
|
953
|
-
|
|
966
|
+
respondThrown(err);
|
|
954
967
|
});
|
|
955
968
|
return;
|
|
956
969
|
}
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
catch (err) {
|
|
960
|
-
sendCallbackError(data.callId, err, ctx, meta.senderInstanceId);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
function handleCallbackInvokeResponse(data, ctx, meta) {
|
|
964
|
-
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
965
|
-
if (!pending)
|
|
966
|
-
return;
|
|
967
|
-
if (data.error) {
|
|
968
|
-
const err = new Error(data.error.message);
|
|
969
|
-
if (data.error.stack)
|
|
970
|
-
err.stack = data.error.stack;
|
|
971
|
-
pending.onReject?.();
|
|
972
|
-
pending.reject(err);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
976
|
-
try {
|
|
977
|
-
pending.onResolve?.();
|
|
978
|
-
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
979
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
980
|
-
}, scopedRemoteCallback));
|
|
970
|
+
respondSuccess(result);
|
|
981
971
|
}
|
|
982
972
|
catch (err) {
|
|
983
|
-
|
|
984
|
-
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
973
|
+
respondThrown(err);
|
|
985
974
|
}
|
|
986
975
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
sendCallbackError(id, err, ctx, targetInstanceId);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
function sendCallbackError(id, err, ctx, targetInstanceId) {
|
|
1000
|
-
ctx
|
|
1001
|
-
.getMessageChannel()
|
|
1002
|
-
.getSender()
|
|
1003
|
-
.sendMessage('invokeFunctionByIdResponse', {
|
|
1004
|
-
id,
|
|
1005
|
-
error: serializeThrownError(err),
|
|
1006
|
-
}, targetInstanceId);
|
|
976
|
+
const handleCallbackInvokeResponse = createResponseHandler('handleCallbackInvokeResponse', warn);
|
|
977
|
+
|
|
978
|
+
const RELEASE_CHUNK_SIZE = 1000;
|
|
979
|
+
const scheduleNextChunk = typeof queueMicrotask === 'function' ? (cb) => queueMicrotask(cb) : (cb) => setTimeout(cb, 0);
|
|
980
|
+
function handleConnectRequest(_data, ctx, meta) {
|
|
981
|
+
ctx.noteFreshConnect(meta.senderInstanceId);
|
|
982
|
+
ctx.getMessageChannel().getSender().sendMessage('connectResponse', {
|
|
983
|
+
instanceId: ctx.getInstanceId(),
|
|
984
|
+
}, meta.senderInstanceId);
|
|
1007
985
|
}
|
|
1008
|
-
function
|
|
1009
|
-
|
|
986
|
+
function handleConnectResponse(_data, ctx, meta) {
|
|
987
|
+
ctx.bindRemote(meta.senderInstanceId);
|
|
1010
988
|
}
|
|
1011
|
-
|
|
1012
|
-
const MAX_RELEASE_IDS = 100000;
|
|
1013
989
|
function handleReleaseCallbacks(data, ctx, meta) {
|
|
1014
990
|
const ids = data.ids;
|
|
1015
|
-
const total = ids.length
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
991
|
+
const total = ids.length;
|
|
992
|
+
ctx.releaseRemoteCallbacks(meta.senderInstanceId, ids);
|
|
993
|
+
let cursor = 0;
|
|
994
|
+
const sender = meta.senderInstanceId;
|
|
995
|
+
const processChunk = () => {
|
|
996
|
+
if (ctx.isDestroyed())
|
|
997
|
+
return;
|
|
998
|
+
const end = Math.min(cursor + RELEASE_CHUNK_SIZE, total);
|
|
999
|
+
for (let i = cursor; i < end; i++) {
|
|
1000
|
+
ctx.dropLocalCallback(ids[i], sender);
|
|
1001
|
+
}
|
|
1002
|
+
cursor = end;
|
|
1003
|
+
if (cursor < total)
|
|
1004
|
+
scheduleNextChunk(processChunk);
|
|
1005
|
+
};
|
|
1006
|
+
processChunk();
|
|
1024
1007
|
}
|
|
1025
|
-
function handleDestroyEndpoint(
|
|
1008
|
+
function handleDestroyEndpoint(data, ctx, meta) {
|
|
1009
|
+
if (data.instanceId !== meta.senderInstanceId)
|
|
1010
|
+
return;
|
|
1026
1011
|
ctx.handleRemoteDestroy(meta.senderInstanceId);
|
|
1027
1012
|
}
|
|
1028
1013
|
|
|
@@ -1043,10 +1028,16 @@ const DEFAULT_FUNCTION_CACHE_MAX = 100;
|
|
|
1043
1028
|
const DEFAULT_FUNCTION_CACHE_TTL = 5 * 60 * 1000;
|
|
1044
1029
|
const DEFAULT_REMOTE_CALLBACK_CACHE_MAX = 500;
|
|
1045
1030
|
const DEFAULT_REMOTE_CALLBACK_CACHE_TTL = 5 * 60 * 1000;
|
|
1031
|
+
const RELEASE_CALLBACKS_CHUNK_SIZE = 10000;
|
|
1032
|
+
const BROADCAST_OWNER = Symbol('chrome-in-iframe.broadcast-owner');
|
|
1046
1033
|
function resolveTimeout(raw) {
|
|
1047
1034
|
if (raw === undefined)
|
|
1048
1035
|
return DEFAULT_TIMEOUT;
|
|
1049
|
-
if (typeof raw !== 'number'
|
|
1036
|
+
if (typeof raw !== 'number')
|
|
1037
|
+
return DEFAULT_TIMEOUT;
|
|
1038
|
+
if (raw === Infinity)
|
|
1039
|
+
return Infinity;
|
|
1040
|
+
if (!Number.isFinite(raw) || raw <= 0)
|
|
1050
1041
|
return DEFAULT_TIMEOUT;
|
|
1051
1042
|
return raw;
|
|
1052
1043
|
}
|
|
@@ -1055,7 +1046,7 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1055
1046
|
const remoteCacheTtl = options.remoteCallbackCacheTtl ?? DEFAULT_REMOTE_CALLBACK_CACHE_TTL;
|
|
1056
1047
|
const callTimeout = resolveTimeout(options.timeout);
|
|
1057
1048
|
const pending = new Map();
|
|
1058
|
-
const functionIds = new
|
|
1049
|
+
const functionIds = new WeakMap();
|
|
1059
1050
|
const persistentFunctionCache = new Map();
|
|
1060
1051
|
const persistentRefcount = new Map();
|
|
1061
1052
|
const functionCache = new LRUCache({
|
|
@@ -1072,7 +1063,71 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1072
1063
|
const remoteCallbacksByOwner = new Map();
|
|
1073
1064
|
const persistentRemoteCallbacksByOwner = new Map();
|
|
1074
1065
|
const boundMethodCache = new WeakMap();
|
|
1066
|
+
const callbackOwners = new Map();
|
|
1067
|
+
const knownRemotes = new Set();
|
|
1068
|
+
const remoteTargetWaiters = new Set();
|
|
1069
|
+
let boundRemoteInstanceId;
|
|
1070
|
+
let remoteTargetWaitDisabled = false;
|
|
1075
1071
|
let destroyed = false;
|
|
1072
|
+
const armTimer = (onTimeout) => {
|
|
1073
|
+
if (callTimeout === Infinity)
|
|
1074
|
+
return null;
|
|
1075
|
+
return setTimeout(onTimeout, callTimeout);
|
|
1076
|
+
};
|
|
1077
|
+
const cancelTimer = (timer) => {
|
|
1078
|
+
if (timer !== null)
|
|
1079
|
+
clearTimeout(timer);
|
|
1080
|
+
};
|
|
1081
|
+
const getPreferredRemote = () => {
|
|
1082
|
+
if (boundRemoteInstanceId !== undefined)
|
|
1083
|
+
return boundRemoteInstanceId;
|
|
1084
|
+
if (knownRemotes.size !== 1)
|
|
1085
|
+
return undefined;
|
|
1086
|
+
return knownRemotes.values().next().value;
|
|
1087
|
+
};
|
|
1088
|
+
const resolveRemoteTargetWaiters = (remoteInstanceId) => {
|
|
1089
|
+
if (remoteTargetWaiters.size === 0)
|
|
1090
|
+
return;
|
|
1091
|
+
const waiters = Array.from(remoteTargetWaiters);
|
|
1092
|
+
remoteTargetWaiters.clear();
|
|
1093
|
+
for (const resolve of waiters)
|
|
1094
|
+
resolve(remoteInstanceId);
|
|
1095
|
+
};
|
|
1096
|
+
const waitForRemoteTarget = () => {
|
|
1097
|
+
const preferred = getPreferredRemote();
|
|
1098
|
+
if (preferred !== undefined)
|
|
1099
|
+
return Promise.resolve(preferred);
|
|
1100
|
+
if (destroyed)
|
|
1101
|
+
return Promise.resolve(undefined);
|
|
1102
|
+
return new Promise((resolve) => {
|
|
1103
|
+
remoteTargetWaiters.add(resolve);
|
|
1104
|
+
});
|
|
1105
|
+
};
|
|
1106
|
+
const recordCallbackOwners = (ids, owner) => {
|
|
1107
|
+
if (ids.length === 0)
|
|
1108
|
+
return;
|
|
1109
|
+
if (owner === 'broadcast') {
|
|
1110
|
+
for (const id of ids)
|
|
1111
|
+
callbackOwners.set(id, BROADCAST_OWNER);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
for (const id of ids) {
|
|
1115
|
+
const cur = callbackOwners.get(id);
|
|
1116
|
+
if (cur === BROADCAST_OWNER)
|
|
1117
|
+
continue;
|
|
1118
|
+
if (cur === undefined) {
|
|
1119
|
+
callbackOwners.set(id, owner);
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
if (typeof cur === 'string') {
|
|
1123
|
+
if (cur === owner)
|
|
1124
|
+
continue;
|
|
1125
|
+
callbackOwners.set(id, new Set([cur, owner]));
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
cur.add(owner);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1076
1131
|
const bindMethod = (fn, owner) => {
|
|
1077
1132
|
if (owner === null || typeof owner !== 'object') {
|
|
1078
1133
|
return (...args) => Reflect.apply(fn, owner, args);
|
|
@@ -1151,7 +1206,44 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1151
1206
|
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1152
1207
|
return id;
|
|
1153
1208
|
};
|
|
1154
|
-
const dropLocalCallback = (id) => {
|
|
1209
|
+
const dropLocalCallback = (id, requesterInstanceId) => {
|
|
1210
|
+
if (requesterInstanceId !== undefined) {
|
|
1211
|
+
const cur = callbackOwners.get(id);
|
|
1212
|
+
if (cur === BROADCAST_OWNER) {
|
|
1213
|
+
const remaining = new Set();
|
|
1214
|
+
for (const r of knownRemotes) {
|
|
1215
|
+
if (r !== requesterInstanceId)
|
|
1216
|
+
remaining.add(r);
|
|
1217
|
+
}
|
|
1218
|
+
if (remaining.size > 0) {
|
|
1219
|
+
callbackOwners.set(id, remaining.size === 1 ? remaining.values().next().value : remaining);
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
else if (typeof cur === 'string') {
|
|
1224
|
+
if (cur !== requesterInstanceId)
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
else if (cur instanceof Set) {
|
|
1228
|
+
if (!cur.has(requesterInstanceId))
|
|
1229
|
+
return;
|
|
1230
|
+
cur.delete(requesterInstanceId);
|
|
1231
|
+
if (cur.size > 1)
|
|
1232
|
+
return;
|
|
1233
|
+
if (cur.size === 1) {
|
|
1234
|
+
callbackOwners.set(id, cur.values().next().value);
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (persistentRefcount.has(id)) {
|
|
1240
|
+
const next = (persistentRefcount.get(id) ?? 0) - 1;
|
|
1241
|
+
if (next > 0) {
|
|
1242
|
+
persistentRefcount.set(id, next);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
callbackOwners.delete(id);
|
|
1155
1247
|
const entry = persistentFunctionCache.get(id) ?? functionCache.get(id);
|
|
1156
1248
|
persistentRefcount.delete(id);
|
|
1157
1249
|
persistentFunctionCache.delete(id);
|
|
@@ -1183,19 +1275,32 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1183
1275
|
if (ids.length === 0)
|
|
1184
1276
|
return;
|
|
1185
1277
|
try {
|
|
1186
|
-
getMessageChannel().getSender()
|
|
1278
|
+
const sender = getMessageChannel().getSender();
|
|
1279
|
+
for (let cursor = 0; cursor < ids.length; cursor += RELEASE_CALLBACKS_CHUNK_SIZE) {
|
|
1280
|
+
sender.sendMessage('releaseCallbacks', { ids: ids.slice(cursor, cursor + RELEASE_CALLBACKS_CHUNK_SIZE) });
|
|
1281
|
+
}
|
|
1187
1282
|
}
|
|
1188
1283
|
catch (err) {
|
|
1189
1284
|
warn('notifyReleaseCallbacks', 'failed to release callbacks', err);
|
|
1190
1285
|
}
|
|
1191
1286
|
};
|
|
1287
|
+
const safeOnReject = (callbacks) => {
|
|
1288
|
+
if (!callbacks.onReject)
|
|
1289
|
+
return;
|
|
1290
|
+
try {
|
|
1291
|
+
callbacks.onReject();
|
|
1292
|
+
}
|
|
1293
|
+
catch (err) {
|
|
1294
|
+
warn('rejectPending', 'onReject hook threw', err);
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1192
1297
|
const rejectPending = (id, error) => {
|
|
1193
1298
|
const callbacks = pending.get(id);
|
|
1194
1299
|
if (!callbacks)
|
|
1195
1300
|
return;
|
|
1196
|
-
|
|
1301
|
+
cancelTimer(callbacks.timer);
|
|
1197
1302
|
pending.delete(id);
|
|
1198
|
-
callbacks
|
|
1303
|
+
safeOnReject(callbacks);
|
|
1199
1304
|
callbacks.reject(error);
|
|
1200
1305
|
};
|
|
1201
1306
|
return {
|
|
@@ -1217,15 +1322,31 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1217
1322
|
registerFunction(fn, thisArg, callbackOptions) {
|
|
1218
1323
|
return registerFunction(fn, thisArg, callbackOptions);
|
|
1219
1324
|
},
|
|
1325
|
+
serializeForRemote(value, target, callbackOptions) {
|
|
1326
|
+
const ids = [];
|
|
1327
|
+
const tracking = (fn, thisArg, opts) => {
|
|
1328
|
+
const id = registerFunction(fn, thisArg, opts);
|
|
1329
|
+
ids.push(id);
|
|
1330
|
+
return id;
|
|
1331
|
+
};
|
|
1332
|
+
const result = serialize(value, tracking, callbackOptions);
|
|
1333
|
+
recordCallbackOwners(ids, target);
|
|
1334
|
+
return result;
|
|
1335
|
+
},
|
|
1220
1336
|
getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId) {
|
|
1221
1337
|
return getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId);
|
|
1222
1338
|
},
|
|
1223
|
-
getAndRemovePendingPromise(id) {
|
|
1339
|
+
getAndRemovePendingPromise(id, senderInstanceId) {
|
|
1224
1340
|
const callbacks = pending.get(id);
|
|
1225
|
-
if (callbacks)
|
|
1226
|
-
|
|
1227
|
-
|
|
1341
|
+
if (!callbacks)
|
|
1342
|
+
return undefined;
|
|
1343
|
+
if (callbacks.expectedRemote !== undefined &&
|
|
1344
|
+
senderInstanceId !== undefined &&
|
|
1345
|
+
callbacks.expectedRemote !== senderInstanceId) {
|
|
1346
|
+
return undefined;
|
|
1228
1347
|
}
|
|
1348
|
+
cancelTimer(callbacks.timer);
|
|
1349
|
+
pending.delete(id);
|
|
1229
1350
|
return callbacks;
|
|
1230
1351
|
},
|
|
1231
1352
|
invoke(path, args) {
|
|
@@ -1251,46 +1372,97 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1251
1372
|
}
|
|
1252
1373
|
return released;
|
|
1253
1374
|
};
|
|
1254
|
-
const
|
|
1375
|
+
const cleanupRegisteredOnFailure = () => {
|
|
1376
|
+
const released = [];
|
|
1377
|
+
for (const cbId of registeredIds) {
|
|
1378
|
+
if (persistentRefcount.has(cbId)) {
|
|
1379
|
+
const releasedId = releasePersistentRegistrationById(cbId);
|
|
1380
|
+
if (releasedId)
|
|
1381
|
+
released.push(releasedId);
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
const entry = functionCache.get(cbId);
|
|
1385
|
+
if (entry) {
|
|
1386
|
+
functionCache.delete(cbId);
|
|
1387
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
callbackOwners.delete(cbId);
|
|
1391
|
+
}
|
|
1392
|
+
return released;
|
|
1393
|
+
};
|
|
1394
|
+
const timer = armTimer(() => {
|
|
1255
1395
|
rejectPending(id, new Error(`Call timed out: ${String(path.join('.'))}`));
|
|
1256
|
-
}
|
|
1396
|
+
});
|
|
1397
|
+
let serializedArgs;
|
|
1257
1398
|
try {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
pending.set(id, {
|
|
1261
|
-
resolve,
|
|
1262
|
-
reject,
|
|
1263
|
-
timer,
|
|
1264
|
-
onResolve: () => {
|
|
1265
|
-
if (isListenerRemovalPath(path)) {
|
|
1266
|
-
const released = releaseRegisteredPersistents();
|
|
1267
|
-
if (released.length > 0)
|
|
1268
|
-
notifyReleaseCallbacks(released);
|
|
1269
|
-
}
|
|
1270
|
-
},
|
|
1271
|
-
onReject: () => {
|
|
1272
|
-
if (isListenerRegistrationPath(path)) {
|
|
1273
|
-
const released = releaseRegisteredPersistents();
|
|
1274
|
-
if (released.length > 0)
|
|
1275
|
-
notifyReleaseCallbacks(released);
|
|
1276
|
-
}
|
|
1277
|
-
},
|
|
1278
|
-
});
|
|
1279
|
-
channel.getSender().sendMessage('invokeRequest', {
|
|
1280
|
-
id,
|
|
1281
|
-
path,
|
|
1282
|
-
args: serializedArgs,
|
|
1283
|
-
});
|
|
1399
|
+
serializePath(path);
|
|
1400
|
+
serializedArgs = args.map((arg) => serialize(arg, collectingRegister, { persistent }));
|
|
1284
1401
|
}
|
|
1285
1402
|
catch (err) {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
if (
|
|
1289
|
-
|
|
1403
|
+
cancelTimer(timer);
|
|
1404
|
+
const released = cleanupRegisteredOnFailure();
|
|
1405
|
+
if (released.length > 0)
|
|
1406
|
+
notifyReleaseCallbacks(released);
|
|
1407
|
+
reject(err);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
pending.set(id, {
|
|
1411
|
+
resolve,
|
|
1412
|
+
reject,
|
|
1413
|
+
timer,
|
|
1414
|
+
onResolve: () => {
|
|
1415
|
+
if (isListenerRemovalPath(path)) {
|
|
1416
|
+
const released = releaseRegisteredPersistents();
|
|
1417
|
+
if (released.length > 0)
|
|
1418
|
+
notifyReleaseCallbacks(released);
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
onReject: () => {
|
|
1422
|
+
if (isListenerRegistrationPath(path)) {
|
|
1423
|
+
const released = releaseRegisteredPersistents();
|
|
1424
|
+
if (released.length > 0)
|
|
1425
|
+
notifyReleaseCallbacks(released);
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
});
|
|
1429
|
+
const sendRequest = (targetInstanceId, requireTarget = true) => {
|
|
1430
|
+
const callbacks = pending.get(id);
|
|
1431
|
+
if (!callbacks)
|
|
1432
|
+
return;
|
|
1433
|
+
if (targetInstanceId === undefined && requireTarget) {
|
|
1434
|
+
rejectPending(id, new Error('No remote endpoint is available'));
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
if (targetInstanceId !== undefined)
|
|
1438
|
+
callbacks.expectedRemote = targetInstanceId;
|
|
1439
|
+
try {
|
|
1440
|
+
const channel = getMessageChannel();
|
|
1441
|
+
recordCallbackOwners(registeredIds, targetInstanceId ?? 'broadcast');
|
|
1442
|
+
channel.getSender().sendMessage('invokeRequest', {
|
|
1443
|
+
id,
|
|
1444
|
+
path,
|
|
1445
|
+
args: serializedArgs,
|
|
1446
|
+
}, targetInstanceId);
|
|
1447
|
+
}
|
|
1448
|
+
catch (err) {
|
|
1449
|
+
cancelTimer(timer);
|
|
1450
|
+
pending.delete(id);
|
|
1451
|
+
const released = cleanupRegisteredOnFailure();
|
|
1290
1452
|
if (released.length > 0)
|
|
1291
1453
|
notifyReleaseCallbacks(released);
|
|
1454
|
+
reject(err);
|
|
1292
1455
|
}
|
|
1293
|
-
|
|
1456
|
+
};
|
|
1457
|
+
const targetInstanceId = getPreferredRemote();
|
|
1458
|
+
if (targetInstanceId !== undefined) {
|
|
1459
|
+
sendRequest(targetInstanceId);
|
|
1460
|
+
}
|
|
1461
|
+
else if (remoteTargetWaitDisabled) {
|
|
1462
|
+
sendRequest(undefined, false);
|
|
1463
|
+
}
|
|
1464
|
+
else {
|
|
1465
|
+
void waitForRemoteTarget().then(sendRequest);
|
|
1294
1466
|
}
|
|
1295
1467
|
});
|
|
1296
1468
|
},
|
|
@@ -1301,46 +1473,100 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1301
1473
|
return;
|
|
1302
1474
|
}
|
|
1303
1475
|
const id = createId('req');
|
|
1304
|
-
const timer =
|
|
1476
|
+
const timer = armTimer(() => {
|
|
1305
1477
|
rejectPending(id, new Error(`Property access timed out: ${String(path.join('.'))}`));
|
|
1306
|
-
}
|
|
1478
|
+
});
|
|
1307
1479
|
pending.set(id, { resolve, reject, timer });
|
|
1480
|
+
let pathSerializationError;
|
|
1308
1481
|
try {
|
|
1309
|
-
|
|
1310
|
-
id,
|
|
1311
|
-
path,
|
|
1312
|
-
});
|
|
1482
|
+
serializePath(path);
|
|
1313
1483
|
}
|
|
1314
1484
|
catch (err) {
|
|
1315
|
-
|
|
1485
|
+
pathSerializationError = err;
|
|
1486
|
+
}
|
|
1487
|
+
if (pathSerializationError !== undefined) {
|
|
1488
|
+
cancelTimer(timer);
|
|
1316
1489
|
pending.delete(id);
|
|
1317
|
-
reject(
|
|
1490
|
+
reject(pathSerializationError);
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
const sendRequest = (targetInstanceId, requireTarget = true) => {
|
|
1494
|
+
const callbacks = pending.get(id);
|
|
1495
|
+
if (!callbacks)
|
|
1496
|
+
return;
|
|
1497
|
+
if (targetInstanceId === undefined && requireTarget) {
|
|
1498
|
+
rejectPending(id, new Error('No remote endpoint is available'));
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
if (targetInstanceId !== undefined)
|
|
1502
|
+
callbacks.expectedRemote = targetInstanceId;
|
|
1503
|
+
try {
|
|
1504
|
+
getMessageChannel().getSender().sendMessage('accessPropertyRequest', {
|
|
1505
|
+
id,
|
|
1506
|
+
path,
|
|
1507
|
+
}, targetInstanceId);
|
|
1508
|
+
}
|
|
1509
|
+
catch (err) {
|
|
1510
|
+
cancelTimer(timer);
|
|
1511
|
+
pending.delete(id);
|
|
1512
|
+
reject(err);
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const targetInstanceId = getPreferredRemote();
|
|
1516
|
+
if (targetInstanceId !== undefined) {
|
|
1517
|
+
sendRequest(targetInstanceId);
|
|
1518
|
+
}
|
|
1519
|
+
else if (remoteTargetWaitDisabled) {
|
|
1520
|
+
sendRequest(undefined, false);
|
|
1521
|
+
}
|
|
1522
|
+
else {
|
|
1523
|
+
void waitForRemoteTarget().then(sendRequest);
|
|
1318
1524
|
}
|
|
1319
1525
|
});
|
|
1320
1526
|
},
|
|
1321
|
-
invokeFunctionById(callbackId, args, callbackOptions = {}) {
|
|
1527
|
+
invokeFunctionById(callbackId, args, callbackOptions = {}, remoteInstanceId) {
|
|
1322
1528
|
return new Promise((resolve, reject) => {
|
|
1323
1529
|
if (destroyed) {
|
|
1324
1530
|
reject(new Error('Channel has been destroyed'));
|
|
1325
1531
|
return;
|
|
1326
1532
|
}
|
|
1327
1533
|
const callId = createId('cbcall');
|
|
1328
|
-
const timer =
|
|
1534
|
+
const timer = armTimer(() => {
|
|
1329
1535
|
rejectPending(callId, new Error(`Callback call timed out: ${callbackId}`));
|
|
1330
|
-
}
|
|
1331
|
-
pending.set(callId, { resolve, reject, timer });
|
|
1536
|
+
});
|
|
1537
|
+
pending.set(callId, { resolve, reject, timer, expectedRemote: remoteInstanceId });
|
|
1538
|
+
const registeredIds = [];
|
|
1539
|
+
const collectingRegister = (fn, thisArg, opts) => {
|
|
1540
|
+
const cbId = registerFunction(fn, thisArg, opts);
|
|
1541
|
+
registeredIds.push(cbId);
|
|
1542
|
+
return cbId;
|
|
1543
|
+
};
|
|
1332
1544
|
try {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1545
|
+
const serializedArgs = args.map((arg) => serialize(arg, collectingRegister, callbackOptions));
|
|
1546
|
+
const targetInstanceId = remoteInstanceId ?? getPreferredRemote();
|
|
1547
|
+
recordCallbackOwners(registeredIds, targetInstanceId ?? 'broadcast');
|
|
1548
|
+
getMessageChannel().getSender().sendMessage('invokeFunctionByIdRequest', {
|
|
1336
1549
|
id: callbackId,
|
|
1337
1550
|
callId,
|
|
1338
|
-
args:
|
|
1339
|
-
});
|
|
1551
|
+
args: serializedArgs,
|
|
1552
|
+
}, targetInstanceId);
|
|
1340
1553
|
}
|
|
1341
1554
|
catch (err) {
|
|
1342
|
-
|
|
1555
|
+
cancelTimer(timer);
|
|
1343
1556
|
pending.delete(callId);
|
|
1557
|
+
for (const cbId of registeredIds) {
|
|
1558
|
+
if (persistentRefcount.has(cbId)) {
|
|
1559
|
+
releasePersistentRegistrationById(cbId);
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
const entry = functionCache.get(cbId);
|
|
1563
|
+
if (entry) {
|
|
1564
|
+
functionCache.delete(cbId);
|
|
1565
|
+
removeFunctionIdMapping(entry.fn, entry.thisArg);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
callbackOwners.delete(cbId);
|
|
1569
|
+
}
|
|
1344
1570
|
reject(err);
|
|
1345
1571
|
}
|
|
1346
1572
|
});
|
|
@@ -1348,6 +1574,62 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1348
1574
|
handleRemoteDestroy(remoteInstanceId) {
|
|
1349
1575
|
remoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1350
1576
|
persistentRemoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1577
|
+
knownRemotes.delete(remoteInstanceId);
|
|
1578
|
+
if (boundRemoteInstanceId === remoteInstanceId) {
|
|
1579
|
+
boundRemoteInstanceId = undefined;
|
|
1580
|
+
const preferred = getPreferredRemote();
|
|
1581
|
+
if (preferred !== undefined)
|
|
1582
|
+
boundRemoteInstanceId = preferred;
|
|
1583
|
+
}
|
|
1584
|
+
for (const [id, owners] of callbackOwners) {
|
|
1585
|
+
if (owners === remoteInstanceId) {
|
|
1586
|
+
callbackOwners.delete(id);
|
|
1587
|
+
}
|
|
1588
|
+
else if (owners instanceof Set && owners.has(remoteInstanceId)) {
|
|
1589
|
+
owners.delete(remoteInstanceId);
|
|
1590
|
+
if (owners.size === 0)
|
|
1591
|
+
callbackOwners.delete(id);
|
|
1592
|
+
else if (owners.size === 1)
|
|
1593
|
+
callbackOwners.set(id, owners.values().next().value);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
const reason = new Error(`Remote endpoint ${remoteInstanceId} was destroyed`);
|
|
1597
|
+
const noKnownRemotesLeft = knownRemotes.size === 0;
|
|
1598
|
+
for (const [id, callbacks] of pending) {
|
|
1599
|
+
const targeted = callbacks.expectedRemote === remoteInstanceId;
|
|
1600
|
+
const stranded = callbacks.expectedRemote === undefined && noKnownRemotesLeft;
|
|
1601
|
+
if (!targeted && !stranded)
|
|
1602
|
+
continue;
|
|
1603
|
+
cancelTimer(callbacks.timer);
|
|
1604
|
+
pending.delete(id);
|
|
1605
|
+
safeOnReject(callbacks);
|
|
1606
|
+
callbacks.reject(reason);
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
bindRemote(remoteInstanceId) {
|
|
1610
|
+
if (!remoteInstanceId || remoteInstanceId === instanceId)
|
|
1611
|
+
return;
|
|
1612
|
+
knownRemotes.add(remoteInstanceId);
|
|
1613
|
+
if (boundRemoteInstanceId === undefined) {
|
|
1614
|
+
boundRemoteInstanceId = remoteInstanceId;
|
|
1615
|
+
resolveRemoteTargetWaiters(remoteInstanceId);
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
noteFreshConnect(remoteInstanceId) {
|
|
1619
|
+
if (!remoteInstanceId || remoteInstanceId === instanceId)
|
|
1620
|
+
return;
|
|
1621
|
+
const isNew = !knownRemotes.has(remoteInstanceId);
|
|
1622
|
+
knownRemotes.add(remoteInstanceId);
|
|
1623
|
+
if (boundRemoteInstanceId === remoteInstanceId)
|
|
1624
|
+
return;
|
|
1625
|
+
if (boundRemoteInstanceId === undefined || isNew) {
|
|
1626
|
+
boundRemoteInstanceId = remoteInstanceId;
|
|
1627
|
+
resolveRemoteTargetWaiters(remoteInstanceId);
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1630
|
+
disableRemoteTargetWait() {
|
|
1631
|
+
remoteTargetWaitDisabled = true;
|
|
1632
|
+
resolveRemoteTargetWaiters(undefined);
|
|
1351
1633
|
},
|
|
1352
1634
|
releaseRemoteCallbacks(remoteInstanceId, ids) {
|
|
1353
1635
|
const lru = remoteCallbacksByOwner.get(remoteInstanceId);
|
|
@@ -1359,8 +1641,16 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1359
1641
|
pmap?.delete(id);
|
|
1360
1642
|
}
|
|
1361
1643
|
},
|
|
1362
|
-
dropLocalCallback(id) {
|
|
1363
|
-
dropLocalCallback(id);
|
|
1644
|
+
dropLocalCallback(id, requesterInstanceId) {
|
|
1645
|
+
dropLocalCallback(id, requesterInstanceId);
|
|
1646
|
+
},
|
|
1647
|
+
noteRemoteSeen(remoteInstanceId) {
|
|
1648
|
+
if (remoteInstanceId && remoteInstanceId !== instanceId) {
|
|
1649
|
+
knownRemotes.add(remoteInstanceId);
|
|
1650
|
+
const preferred = getPreferredRemote();
|
|
1651
|
+
if (preferred !== undefined)
|
|
1652
|
+
resolveRemoteTargetWaiters(preferred);
|
|
1653
|
+
}
|
|
1364
1654
|
},
|
|
1365
1655
|
bindMethod(fn, owner) {
|
|
1366
1656
|
return bindMethod(fn, owner);
|
|
@@ -1371,42 +1661,48 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1371
1661
|
persistent: persistentRemoteCallbacksByOwner.get(remoteInstanceId)?.size ?? 0,
|
|
1372
1662
|
};
|
|
1373
1663
|
},
|
|
1664
|
+
isDestroyed() {
|
|
1665
|
+
return destroyed;
|
|
1666
|
+
},
|
|
1374
1667
|
destroy(notifyRemote = true) {
|
|
1375
1668
|
if (destroyed)
|
|
1376
1669
|
return;
|
|
1377
1670
|
destroyed = true;
|
|
1378
1671
|
if (notifyRemote) {
|
|
1379
|
-
const
|
|
1672
|
+
const heldRemoteIdSet = new Set();
|
|
1380
1673
|
for (const lru of remoteCallbacksByOwner.values()) {
|
|
1381
1674
|
for (const id of lru.keys())
|
|
1382
|
-
|
|
1675
|
+
heldRemoteIdSet.add(id);
|
|
1383
1676
|
}
|
|
1384
1677
|
for (const map of persistentRemoteCallbacksByOwner.values()) {
|
|
1385
1678
|
for (const id of map.keys())
|
|
1386
|
-
|
|
1679
|
+
heldRemoteIdSet.add(id);
|
|
1680
|
+
}
|
|
1681
|
+
if (heldRemoteIdSet.size > 0) {
|
|
1682
|
+
notifyReleaseCallbacks(Array.from(heldRemoteIdSet));
|
|
1387
1683
|
}
|
|
1388
1684
|
try {
|
|
1389
|
-
|
|
1390
|
-
if (heldRemoteIds.length > 0) {
|
|
1391
|
-
sender.sendMessage('releaseCallbacks', { ids: heldRemoteIds });
|
|
1392
|
-
}
|
|
1393
|
-
sender.sendMessage('destroyEndpoint', { instanceId });
|
|
1685
|
+
getMessageChannel().getSender().sendMessage('destroyEndpoint', { instanceId });
|
|
1394
1686
|
}
|
|
1395
1687
|
catch (err) {
|
|
1396
|
-
if (!
|
|
1688
|
+
if (!isTransportDetachedError(err)) {
|
|
1397
1689
|
warn('destroy', 'failed to notify remote of destroy', err);
|
|
1398
1690
|
}
|
|
1399
1691
|
}
|
|
1400
1692
|
}
|
|
1401
1693
|
for (const [id, callbacks] of pending) {
|
|
1402
|
-
|
|
1694
|
+
cancelTimer(callbacks.timer);
|
|
1695
|
+
safeOnReject(callbacks);
|
|
1403
1696
|
callbacks.reject(new Error(`Channel has been destroyed before call ${id} completed`));
|
|
1404
1697
|
}
|
|
1405
1698
|
pending.clear();
|
|
1406
1699
|
functionCache.clear();
|
|
1407
1700
|
persistentFunctionCache.clear();
|
|
1408
1701
|
persistentRefcount.clear();
|
|
1409
|
-
|
|
1702
|
+
callbackOwners.clear();
|
|
1703
|
+
knownRemotes.clear();
|
|
1704
|
+
boundRemoteInstanceId = undefined;
|
|
1705
|
+
resolveRemoteTargetWaiters(undefined);
|
|
1410
1706
|
remoteCallbacksByOwner.clear();
|
|
1411
1707
|
persistentRemoteCallbacksByOwner.clear();
|
|
1412
1708
|
},
|
|
@@ -1415,9 +1711,6 @@ function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
|
1415
1711
|
function createId(prefix) {
|
|
1416
1712
|
return `${prefix}-${nanoid()}`;
|
|
1417
1713
|
}
|
|
1418
|
-
function isExpectedDestroyNotifyError(err) {
|
|
1419
|
-
return err instanceof Error && err.message.includes('contentWindow is not available');
|
|
1420
|
-
}
|
|
1421
1714
|
|
|
1422
1715
|
const EMPTY_TARGET = () => undefined;
|
|
1423
1716
|
const INTERCEPTED_STRING_PROPS = new Set([
|
|
@@ -1481,38 +1774,55 @@ function createClientProxy(invoke, accessProperty) {
|
|
|
1481
1774
|
});
|
|
1482
1775
|
return [...basePath, ...extraPath];
|
|
1483
1776
|
};
|
|
1484
|
-
function
|
|
1485
|
-
|
|
1777
|
+
function createNode(node) {
|
|
1778
|
+
const stringChildren = new Map();
|
|
1779
|
+
const symbolChildren = new Map();
|
|
1780
|
+
let getCache;
|
|
1781
|
+
const handler = {
|
|
1486
1782
|
get(_target, prop) {
|
|
1487
1783
|
if (typeof prop === 'string') {
|
|
1488
1784
|
if (INTERCEPTED_STRING_PROPS.has(prop))
|
|
1489
1785
|
return undefined;
|
|
1490
1786
|
if (prop === '$get') {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1787
|
+
if (!getCache) {
|
|
1788
|
+
getCache = (...parts) => {
|
|
1789
|
+
if (!accessProperty) {
|
|
1790
|
+
return Promise.reject(new Error('Property access is not configured'));
|
|
1791
|
+
}
|
|
1792
|
+
const basePath = node ? resolvePath(node) : [];
|
|
1793
|
+
return accessProperty(normalizePropertyPath(basePath, parts));
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
return getCache;
|
|
1498
1797
|
}
|
|
1798
|
+
const cached = stringChildren.get(prop);
|
|
1799
|
+
if (cached !== undefined)
|
|
1800
|
+
return cached;
|
|
1801
|
+
const childNode = node ? { parent: node, key: prop } : { key: prop };
|
|
1802
|
+
const child = createNode(childNode);
|
|
1803
|
+
stringChildren.set(prop, child);
|
|
1804
|
+
return child;
|
|
1499
1805
|
}
|
|
1500
|
-
|
|
1806
|
+
if (prop === Symbol.toPrimitive)
|
|
1501
1807
|
return toPrimitiveDescriptor();
|
|
1502
|
-
|
|
1503
|
-
else if (INTERCEPTED_NONE_SYMBOLS.has(prop)) {
|
|
1808
|
+
if (INTERCEPTED_NONE_SYMBOLS.has(prop))
|
|
1504
1809
|
return undefined;
|
|
1505
|
-
|
|
1810
|
+
const cached = symbolChildren.get(prop);
|
|
1811
|
+
if (cached !== undefined)
|
|
1812
|
+
return cached;
|
|
1506
1813
|
const childNode = node ? { parent: node, key: prop } : { key: prop };
|
|
1507
|
-
|
|
1814
|
+
const child = createNode(childNode);
|
|
1815
|
+
symbolChildren.set(prop, child);
|
|
1816
|
+
return child;
|
|
1508
1817
|
},
|
|
1509
1818
|
apply(_target, _thisArg, argArray) {
|
|
1510
1819
|
const path = node ? resolvePath(node) : [];
|
|
1511
1820
|
return invoke(path, argArray);
|
|
1512
1821
|
},
|
|
1513
1822
|
};
|
|
1823
|
+
return new Proxy(EMPTY_TARGET, handler);
|
|
1514
1824
|
}
|
|
1515
|
-
return
|
|
1825
|
+
return createNode();
|
|
1516
1826
|
}
|
|
1517
1827
|
function isPathKey(value) {
|
|
1518
1828
|
return typeof value === 'string' || typeof value === 'symbol';
|
|
@@ -1531,6 +1841,8 @@ function createEndpoint(options) {
|
|
|
1531
1841
|
const context = createClientContext(getChannel, instanceId, options.contextOptions);
|
|
1532
1842
|
const channel = createMessageChannel(options.poster, options.key ?? 'default', instanceId, context, processorRegistry);
|
|
1533
1843
|
channelRef.current = channel;
|
|
1844
|
+
processorRegistry.register('connectRequest', handleConnectRequest);
|
|
1845
|
+
processorRegistry.register('connectResponse', handleConnectResponse);
|
|
1534
1846
|
processorRegistry.register('invokeRequest', handleInvokeRequest);
|
|
1535
1847
|
processorRegistry.register('invokeResponse', handleInvokeResponse);
|
|
1536
1848
|
processorRegistry.register('accessPropertyRequest', handleAccessPropertyRequest);
|
|
@@ -1540,67 +1852,64 @@ function createEndpoint(options) {
|
|
|
1540
1852
|
processorRegistry.register('releaseCallbacks', handleReleaseCallbacks);
|
|
1541
1853
|
processorRegistry.register('destroyEndpoint', handleDestroyEndpoint);
|
|
1542
1854
|
const proxy = createClientProxy((path, args) => context.invoke(path, args), (path) => context.accessProperty(path));
|
|
1855
|
+
try {
|
|
1856
|
+
channel.getSender().sendMessage('connectRequest', { instanceId });
|
|
1857
|
+
}
|
|
1858
|
+
catch {
|
|
1859
|
+
context.disableRemoteTargetWait();
|
|
1860
|
+
}
|
|
1543
1861
|
let destroyed = false;
|
|
1544
1862
|
return {
|
|
1545
1863
|
proxy,
|
|
1546
1864
|
getContext: () => context,
|
|
1865
|
+
isDestroyed: () => destroyed,
|
|
1547
1866
|
destroy() {
|
|
1548
1867
|
if (destroyed)
|
|
1549
1868
|
return;
|
|
1550
1869
|
destroyed = true;
|
|
1551
|
-
context.destroy(true);
|
|
1552
1870
|
channel.destroy();
|
|
1871
|
+
context.destroy(true);
|
|
1553
1872
|
},
|
|
1554
1873
|
};
|
|
1555
1874
|
}
|
|
1556
1875
|
|
|
1557
1876
|
function setupInMainWindow(options) {
|
|
1558
|
-
const resolveTargetOrigin = createTargetOriginResolver(() => options.iframe, options.targetOrigin);
|
|
1559
|
-
|
|
1560
|
-
|
|
1877
|
+
const resolveTargetOrigin = createTargetOriginResolver(() => options.iframe, options.targetOrigin, options.strictOrigin === true);
|
|
1878
|
+
return setupConnection(options, {
|
|
1879
|
+
resolveTargetOrigin,
|
|
1561
1880
|
postMessage: (message) => {
|
|
1562
1881
|
const contentWindow = options.iframe.contentWindow;
|
|
1563
1882
|
if (!contentWindow) {
|
|
1564
|
-
throw
|
|
1883
|
+
throw createTransportDetachedError('chrome-in-iframe: iframe contentWindow is not available');
|
|
1565
1884
|
}
|
|
1566
1885
|
contentWindow.postMessage(message, resolveTargetOrigin());
|
|
1567
1886
|
},
|
|
1568
1887
|
getExpectedSource: () => options.iframe.contentWindow,
|
|
1569
|
-
|
|
1888
|
+
delegateTarget: options.delegateTarget,
|
|
1570
1889
|
});
|
|
1571
|
-
const endpoint = createEndpoint({
|
|
1572
|
-
poster,
|
|
1573
|
-
key: options.key,
|
|
1574
|
-
contextOptions: {
|
|
1575
|
-
delegateTarget: options.delegateTarget,
|
|
1576
|
-
timeout: options.timeout,
|
|
1577
|
-
functionCacheMax: options.functionCacheMax,
|
|
1578
|
-
functionCacheTtl: options.functionCacheTtl,
|
|
1579
|
-
remoteCallbackCacheMax: options.remoteCallbackCacheMax,
|
|
1580
|
-
remoteCallbackCacheTtl: options.remoteCallbackCacheTtl,
|
|
1581
|
-
},
|
|
1582
|
-
});
|
|
1583
|
-
const proxy = endpoint.proxy;
|
|
1584
|
-
return {
|
|
1585
|
-
proxy,
|
|
1586
|
-
get: proxy.$get,
|
|
1587
|
-
destroy: endpoint.destroy,
|
|
1588
|
-
};
|
|
1589
1890
|
}
|
|
1590
1891
|
function setupInIframe(options = {}) {
|
|
1591
|
-
const resolveTargetOrigin = createParentTargetOriginResolver(options.targetOrigin);
|
|
1592
|
-
|
|
1593
|
-
|
|
1892
|
+
const resolveTargetOrigin = createParentTargetOriginResolver(options.targetOrigin, options.strictOrigin === true);
|
|
1893
|
+
return setupConnection(options, {
|
|
1894
|
+
resolveTargetOrigin,
|
|
1594
1895
|
postMessage: (message) => {
|
|
1595
1896
|
window.parent.postMessage(message, resolveTargetOrigin());
|
|
1596
1897
|
},
|
|
1597
1898
|
getExpectedSource: () => window.parent,
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
function setupConnection(options, sides) {
|
|
1902
|
+
const allowedOrigin = createAllowedOrigin(options.allowedOrigin, sides.resolveTargetOrigin);
|
|
1903
|
+
const poster = createWindowPoster({
|
|
1904
|
+
postMessage: sides.postMessage,
|
|
1905
|
+
getExpectedSource: sides.getExpectedSource,
|
|
1598
1906
|
allowedOrigin,
|
|
1599
1907
|
});
|
|
1600
1908
|
const endpoint = createEndpoint({
|
|
1601
1909
|
poster,
|
|
1602
1910
|
key: options.key,
|
|
1603
1911
|
contextOptions: {
|
|
1912
|
+
delegateTarget: sides.delegateTarget,
|
|
1604
1913
|
timeout: options.timeout,
|
|
1605
1914
|
functionCacheMax: options.functionCacheMax,
|
|
1606
1915
|
functionCacheTtl: options.functionCacheTtl,
|
|
@@ -1613,6 +1922,7 @@ function setupInIframe(options = {}) {
|
|
|
1613
1922
|
proxy,
|
|
1614
1923
|
get: proxy.$get,
|
|
1615
1924
|
destroy: endpoint.destroy,
|
|
1925
|
+
isDestroyed: endpoint.isDestroyed,
|
|
1616
1926
|
};
|
|
1617
1927
|
}
|
|
1618
1928
|
function exposeChromeInIframe(options) {
|
|
@@ -1629,46 +1939,85 @@ function connectChromeInIframe(options = {}) {
|
|
|
1629
1939
|
};
|
|
1630
1940
|
}
|
|
1631
1941
|
function createWindowPoster(options) {
|
|
1632
|
-
const
|
|
1633
|
-
const getOrCreatePerName = (name) => {
|
|
1634
|
-
let perName = listenerMap.get(name);
|
|
1635
|
-
if (!perName) {
|
|
1636
|
-
perName = new Map();
|
|
1637
|
-
listenerMap.set(name, perName);
|
|
1638
|
-
}
|
|
1639
|
-
return perName;
|
|
1640
|
-
};
|
|
1942
|
+
const subscriptions = new Map();
|
|
1641
1943
|
return {
|
|
1642
1944
|
postMessage(message) {
|
|
1643
1945
|
options.postMessage(message);
|
|
1644
1946
|
},
|
|
1645
1947
|
addEventListener(name, callback) {
|
|
1646
|
-
const
|
|
1647
|
-
const existing = perName.get(callback);
|
|
1948
|
+
const existing = subscriptions.get(callback);
|
|
1648
1949
|
if (existing)
|
|
1649
|
-
|
|
1650
|
-
const
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
if (!isAllowedOrigin(messageEvent.origin, options.allowedOrigin))
|
|
1655
|
-
return;
|
|
1656
|
-
callback(messageEvent);
|
|
1950
|
+
windowDispatcher.remove(name, existing);
|
|
1951
|
+
const sub = {
|
|
1952
|
+
isExpectedSource: (source) => source === options.getExpectedSource(),
|
|
1953
|
+
isAllowedOrigin: (origin) => isAllowedOrigin(origin, options.allowedOrigin),
|
|
1954
|
+
deliver: callback,
|
|
1657
1955
|
};
|
|
1658
|
-
|
|
1659
|
-
|
|
1956
|
+
subscriptions.set(callback, sub);
|
|
1957
|
+
windowDispatcher.add(name, sub);
|
|
1660
1958
|
},
|
|
1661
1959
|
removeEventListener(name, callback) {
|
|
1662
|
-
const
|
|
1663
|
-
if (!
|
|
1960
|
+
const sub = subscriptions.get(callback);
|
|
1961
|
+
if (!sub)
|
|
1962
|
+
return;
|
|
1963
|
+
windowDispatcher.remove(name, sub);
|
|
1964
|
+
subscriptions.delete(callback);
|
|
1965
|
+
},
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
const windowDispatcher = createWindowDispatcher();
|
|
1969
|
+
function createWindowDispatcher() {
|
|
1970
|
+
const perName = new Map();
|
|
1971
|
+
const domListeners = new Map();
|
|
1972
|
+
const ensureDomListener = (name) => {
|
|
1973
|
+
if (domListeners.has(name))
|
|
1974
|
+
return;
|
|
1975
|
+
if (typeof window === 'undefined')
|
|
1976
|
+
return;
|
|
1977
|
+
const handler = (event) => {
|
|
1978
|
+
const messageEvent = event;
|
|
1979
|
+
const subs = perName.get(name);
|
|
1980
|
+
if (!subs || subs.size === 0)
|
|
1981
|
+
return;
|
|
1982
|
+
for (const sub of subs) {
|
|
1983
|
+
if (!sub.isExpectedSource(messageEvent.source))
|
|
1984
|
+
continue;
|
|
1985
|
+
if (!sub.isAllowedOrigin(messageEvent.origin))
|
|
1986
|
+
continue;
|
|
1987
|
+
try {
|
|
1988
|
+
sub.deliver({ data: messageEvent.data });
|
|
1989
|
+
}
|
|
1990
|
+
catch (err) {
|
|
1991
|
+
warn('windowDispatcher', 'subscriber threw while delivering message', err);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
domListeners.set(name, handler);
|
|
1996
|
+
window.addEventListener(name, handler);
|
|
1997
|
+
};
|
|
1998
|
+
return {
|
|
1999
|
+
add(name, sub) {
|
|
2000
|
+
let subs = perName.get(name);
|
|
2001
|
+
if (!subs) {
|
|
2002
|
+
subs = new Set();
|
|
2003
|
+
perName.set(name, subs);
|
|
2004
|
+
}
|
|
2005
|
+
subs.add(sub);
|
|
2006
|
+
ensureDomListener(name);
|
|
2007
|
+
},
|
|
2008
|
+
remove(name, sub) {
|
|
2009
|
+
const subs = perName.get(name);
|
|
2010
|
+
if (!subs)
|
|
1664
2011
|
return;
|
|
1665
|
-
|
|
1666
|
-
if (
|
|
2012
|
+
subs.delete(sub);
|
|
2013
|
+
if (subs.size > 0)
|
|
1667
2014
|
return;
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
if (
|
|
1671
|
-
|
|
2015
|
+
perName.delete(name);
|
|
2016
|
+
const handler = domListeners.get(name);
|
|
2017
|
+
if (handler && typeof window !== 'undefined') {
|
|
2018
|
+
window.removeEventListener(name, handler);
|
|
2019
|
+
}
|
|
2020
|
+
domListeners.delete(name);
|
|
1672
2021
|
},
|
|
1673
2022
|
};
|
|
1674
2023
|
}
|
|
@@ -1686,27 +2035,46 @@ function createAllowedOrigin(explicit, resolveTargetOrigin) {
|
|
|
1686
2035
|
return explicit;
|
|
1687
2036
|
return (origin) => {
|
|
1688
2037
|
const targetOrigin = resolveTargetOrigin();
|
|
1689
|
-
|
|
2038
|
+
if (targetOrigin === '*') {
|
|
2039
|
+
if (typeof location !== 'undefined' && location.origin && location.origin !== 'null') {
|
|
2040
|
+
return origin === location.origin;
|
|
2041
|
+
}
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
return targetOrigin === origin;
|
|
1690
2045
|
};
|
|
1691
2046
|
}
|
|
1692
2047
|
function getGlobalChrome() {
|
|
1693
2048
|
const chromeApi = globalThis.chrome;
|
|
1694
2049
|
if (!chromeApi) {
|
|
1695
|
-
throw new Error('chrome-in-iframe: global chrome API is not available'
|
|
2050
|
+
throw new Error('chrome-in-iframe: global `chrome` API is not available. ' +
|
|
2051
|
+
'Make sure this runs inside a Chrome extension page, or pass a custom ' +
|
|
2052
|
+
'`chromeApi` option (e.g. for tests / non-extension environments).');
|
|
1696
2053
|
}
|
|
1697
2054
|
return chromeApi;
|
|
1698
2055
|
}
|
|
1699
|
-
function createTargetOriginResolver(getIframe, explicit) {
|
|
2056
|
+
function createTargetOriginResolver(getIframe, explicit, strict) {
|
|
1700
2057
|
if (explicit)
|
|
1701
2058
|
return () => explicit;
|
|
1702
|
-
return () => deriveOriginFromIframe(getIframe());
|
|
2059
|
+
return freezeResolvedOrigin(() => deriveOriginFromIframe(getIframe(), strict === true));
|
|
1703
2060
|
}
|
|
1704
|
-
function createParentTargetOriginResolver(explicit) {
|
|
2061
|
+
function createParentTargetOriginResolver(explicit, strict) {
|
|
1705
2062
|
if (explicit)
|
|
1706
2063
|
return () => explicit;
|
|
1707
|
-
return () => deriveParentOrigin();
|
|
2064
|
+
return freezeResolvedOrigin(() => deriveParentOrigin(strict === true));
|
|
2065
|
+
}
|
|
2066
|
+
function freezeResolvedOrigin(resolve) {
|
|
2067
|
+
let frozen;
|
|
2068
|
+
return () => {
|
|
2069
|
+
if (frozen !== undefined)
|
|
2070
|
+
return frozen;
|
|
2071
|
+
const resolved = resolve();
|
|
2072
|
+
if (resolved !== '*')
|
|
2073
|
+
frozen = resolved;
|
|
2074
|
+
return resolved;
|
|
2075
|
+
};
|
|
1708
2076
|
}
|
|
1709
|
-
function deriveOriginFromIframe(iframe) {
|
|
2077
|
+
function deriveOriginFromIframe(iframe, strict) {
|
|
1710
2078
|
const src = iframe.src || iframe.getAttribute('src') || '';
|
|
1711
2079
|
const origin = parseOrigin(src);
|
|
1712
2080
|
if (origin)
|
|
@@ -1721,11 +2089,14 @@ function deriveOriginFromIframe(iframe) {
|
|
|
1721
2089
|
catch (err) {
|
|
1722
2090
|
warn('deriveOriginFromIframe', 'unable to access cross-origin contentWindow.location', err);
|
|
1723
2091
|
}
|
|
2092
|
+
if (strict) {
|
|
2093
|
+
throw new Error('chrome-in-iframe: unable to derive iframe origin automatically; pass `targetOrigin` explicitly or set `strictOrigin: false`.');
|
|
2094
|
+
}
|
|
1724
2095
|
warn('deriveOriginFromIframe', "unable to derive iframe origin automatically; falling back to '*'. " +
|
|
1725
2096
|
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
1726
2097
|
return '*';
|
|
1727
2098
|
}
|
|
1728
|
-
function deriveParentOrigin() {
|
|
2099
|
+
function deriveParentOrigin(strict) {
|
|
1729
2100
|
if (typeof document !== 'undefined') {
|
|
1730
2101
|
const referrer = document.referrer;
|
|
1731
2102
|
const origin = parseOrigin(referrer);
|
|
@@ -1737,6 +2108,9 @@ function deriveParentOrigin() {
|
|
|
1737
2108
|
if (first && first !== 'null')
|
|
1738
2109
|
return first;
|
|
1739
2110
|
}
|
|
2111
|
+
if (strict) {
|
|
2112
|
+
throw new Error('chrome-in-iframe: unable to derive parent origin automatically; pass `targetOrigin` explicitly or set `strictOrigin: false`.');
|
|
2113
|
+
}
|
|
1740
2114
|
warn('deriveParentOrigin', "unable to derive parent origin automatically; falling back to '*'. " +
|
|
1741
2115
|
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
1742
2116
|
return '*';
|
|
@@ -1756,4 +2130,4 @@ function parseOrigin(url) {
|
|
|
1756
2130
|
return null;
|
|
1757
2131
|
}
|
|
1758
2132
|
|
|
1759
|
-
export { connectChromeInIframe, exposeChromeInIframe, setLogger, setupInIframe, setupInMainWindow };
|
|
2133
|
+
export { TRANSPORT_DETACHED, connectChromeInIframe, exposeChromeInIframe, isTransportDetachedError, setLogger, setupInIframe, setupInMainWindow };
|