chrome-in-iframe 1.0.0 → 2.0.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 +140 -0
- package/CHANGELOG.zh-CN.md +72 -0
- package/LICENSE +15 -0
- package/README.md +91 -13
- package/README.zh-CN.md +93 -14
- package/dist/api.d.ts +4 -0
- package/dist/channel/channel.d.ts +1 -1
- package/dist/channel/deserializer.d.ts +3 -1
- package/dist/channel/listener.d.ts +3 -2
- package/dist/channel/sender.d.ts +1 -1
- package/dist/channel/serializer.d.ts +3 -1
- package/dist/channel/types.d.ts +25 -7
- package/dist/client/context.d.ts +5 -1
- package/dist/client/endpoint.d.ts +3 -1
- package/dist/index.js +772 -237
- package/dist/processor/accessProperty.d.ts +3 -3
- package/dist/processor/invoke.d.ts +3 -3
- package/dist/processor/invokeCallback.d.ts +3 -3
- package/dist/processor/lifecycle.d.ts +3 -0
- package/package.json +15 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { LRUCache } from 'lru-cache';
|
|
2
1
|
import { nanoid } from 'nanoid';
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
3
|
|
|
4
4
|
const WELL_KNOWN_SYMBOLS = {
|
|
5
5
|
asyncIterator: Symbol.asyncIterator,
|
|
@@ -82,10 +82,17 @@ function deserializeSymbol$1(value) {
|
|
|
82
82
|
throw new TypeError('Cannot deserialize symbol path key');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
function createMessageSender(poster, key) {
|
|
85
|
+
function createMessageSender(poster, key, instanceId) {
|
|
86
86
|
return {
|
|
87
|
-
sendMessage(type, data) {
|
|
88
|
-
const body = {
|
|
87
|
+
sendMessage(type, data, targetInstanceId) {
|
|
88
|
+
const body = {
|
|
89
|
+
type,
|
|
90
|
+
key,
|
|
91
|
+
data: serializeMessageData(type, data),
|
|
92
|
+
senderInstanceId: instanceId,
|
|
93
|
+
};
|
|
94
|
+
if (targetInstanceId)
|
|
95
|
+
body.targetInstanceId = targetInstanceId;
|
|
89
96
|
poster.postMessage(JSON.stringify(body));
|
|
90
97
|
},
|
|
91
98
|
};
|
|
@@ -108,8 +115,8 @@ function serializeMessageData(type, data) {
|
|
|
108
115
|
return data;
|
|
109
116
|
}
|
|
110
117
|
|
|
111
|
-
function createMessageChannel(poster, key, context, processorRegistry) {
|
|
112
|
-
const sender = createMessageSender(poster, key);
|
|
118
|
+
function createMessageChannel(poster, key, instanceId, context, processorRegistry) {
|
|
119
|
+
const sender = createMessageSender(poster, key, instanceId);
|
|
113
120
|
const listener = (event) => {
|
|
114
121
|
let body;
|
|
115
122
|
try {
|
|
@@ -122,11 +129,16 @@ function createMessageChannel(poster, key, context, processorRegistry) {
|
|
|
122
129
|
return;
|
|
123
130
|
if (body.key !== key)
|
|
124
131
|
return;
|
|
132
|
+
if (body.senderInstanceId === instanceId)
|
|
133
|
+
return;
|
|
134
|
+
if (body.targetInstanceId !== undefined && body.targetInstanceId !== instanceId)
|
|
135
|
+
return;
|
|
125
136
|
const handler = processorRegistry.get(body.type);
|
|
126
137
|
if (!handler)
|
|
127
138
|
return;
|
|
139
|
+
const meta = { senderInstanceId: body.senderInstanceId };
|
|
128
140
|
try {
|
|
129
|
-
handler(deserializeMessageData(body), context);
|
|
141
|
+
handler(deserializeMessageData(body), context, meta);
|
|
130
142
|
}
|
|
131
143
|
catch (err) {
|
|
132
144
|
sendProcessorError(body, sender, err);
|
|
@@ -146,6 +158,9 @@ function createMessageChannel(poster, key, context, processorRegistry) {
|
|
|
146
158
|
getKey() {
|
|
147
159
|
return key;
|
|
148
160
|
},
|
|
161
|
+
getInstanceId() {
|
|
162
|
+
return instanceId;
|
|
163
|
+
},
|
|
149
164
|
destroy() {
|
|
150
165
|
poster.removeEventListener('message', listener);
|
|
151
166
|
},
|
|
@@ -158,6 +173,10 @@ function isMessageBody(value) {
|
|
|
158
173
|
return false;
|
|
159
174
|
if (typeof value.key !== 'string')
|
|
160
175
|
return false;
|
|
176
|
+
if (typeof value.senderInstanceId !== 'string')
|
|
177
|
+
return false;
|
|
178
|
+
if ('targetInstanceId' in value && typeof value.targetInstanceId !== 'string' && value.targetInstanceId !== undefined)
|
|
179
|
+
return false;
|
|
161
180
|
switch (value.type) {
|
|
162
181
|
case 'invokeRequest':
|
|
163
182
|
return isInvokeRequest(value.data);
|
|
@@ -171,6 +190,10 @@ function isMessageBody(value) {
|
|
|
171
190
|
return isCallbackRequest(value.data);
|
|
172
191
|
case 'invokeFunctionByIdResponse':
|
|
173
192
|
return isResponse(value.data);
|
|
193
|
+
case 'releaseCallbacks':
|
|
194
|
+
return isReleaseCallbacks(value.data);
|
|
195
|
+
case 'destroyEndpoint':
|
|
196
|
+
return isDestroyEndpoint(value.data);
|
|
174
197
|
default:
|
|
175
198
|
return false;
|
|
176
199
|
}
|
|
@@ -182,7 +205,7 @@ function isAccessPropertyRequest(value) {
|
|
|
182
205
|
return isRecord(value) && isMessageId(value.id) && isSerializedPath(value.path);
|
|
183
206
|
}
|
|
184
207
|
function isCallbackRequest(value) {
|
|
185
|
-
return isRecord(value) &&
|
|
208
|
+
return isRecord(value) && isMessageId(value.id) && isMessageId(value.callId) && Array.isArray(value.args);
|
|
186
209
|
}
|
|
187
210
|
function isResponse(value) {
|
|
188
211
|
return (isRecord(value) &&
|
|
@@ -190,6 +213,12 @@ function isResponse(value) {
|
|
|
190
213
|
(!('error' in value) || isSerializedError(value.error)) &&
|
|
191
214
|
('data' in value || 'error' in value));
|
|
192
215
|
}
|
|
216
|
+
function isReleaseCallbacks(value) {
|
|
217
|
+
return isRecord(value) && Array.isArray(value.ids) && value.ids.every((id) => typeof id === 'string');
|
|
218
|
+
}
|
|
219
|
+
function isDestroyEndpoint(value) {
|
|
220
|
+
return isRecord(value) && typeof value.instanceId === 'string';
|
|
221
|
+
}
|
|
193
222
|
function isSerializedError(value) {
|
|
194
223
|
return (isRecord(value) && typeof value.message === 'string' && (!('stack' in value) || typeof value.stack === 'string'));
|
|
195
224
|
}
|
|
@@ -222,7 +251,7 @@ function sendProcessorError(body, sender, err) {
|
|
|
222
251
|
sender.sendMessage('invokeResponse', {
|
|
223
252
|
id: data.id,
|
|
224
253
|
error: serializeThrownError(err),
|
|
225
|
-
});
|
|
254
|
+
}, body.senderInstanceId);
|
|
226
255
|
return;
|
|
227
256
|
}
|
|
228
257
|
if (body.type === 'accessPropertyRequest') {
|
|
@@ -230,7 +259,7 @@ function sendProcessorError(body, sender, err) {
|
|
|
230
259
|
sender.sendMessage('accessPropertyResponse', {
|
|
231
260
|
id: data.id,
|
|
232
261
|
error: serializeThrownError(err),
|
|
233
|
-
});
|
|
262
|
+
}, body.senderInstanceId);
|
|
234
263
|
return;
|
|
235
264
|
}
|
|
236
265
|
if (body.type === 'invokeFunctionByIdRequest') {
|
|
@@ -240,103 +269,151 @@ function sendProcessorError(body, sender, err) {
|
|
|
240
269
|
sender.sendMessage('invokeFunctionByIdResponse', {
|
|
241
270
|
id: data.callId,
|
|
242
271
|
error,
|
|
243
|
-
});
|
|
272
|
+
}, body.senderInstanceId);
|
|
244
273
|
}
|
|
245
274
|
}
|
|
246
275
|
|
|
247
|
-
|
|
248
|
-
|
|
276
|
+
const TYPE_KEY = '$cii$';
|
|
277
|
+
function serialize(arg, registerFunction, options = {}) {
|
|
278
|
+
return serialize0(arg, undefined, registerFunction, options, new WeakSet());
|
|
249
279
|
}
|
|
250
|
-
function
|
|
251
|
-
if (
|
|
252
|
-
return
|
|
280
|
+
function serialize0(arg, owner, registerFunction, options, seen) {
|
|
281
|
+
if (arg === undefined)
|
|
282
|
+
return { [TYPE_KEY]: 'undef' };
|
|
283
|
+
if (arg === null)
|
|
284
|
+
return null;
|
|
285
|
+
const typeOf = typeof arg;
|
|
286
|
+
if (typeOf === 'string')
|
|
287
|
+
return arg;
|
|
288
|
+
if (typeOf === 'boolean')
|
|
289
|
+
return arg;
|
|
290
|
+
if (typeOf === 'number')
|
|
291
|
+
return serializeNumber(arg);
|
|
292
|
+
if (typeOf === 'bigint')
|
|
293
|
+
return { [TYPE_KEY]: 'big', v: arg.toString() };
|
|
294
|
+
if (typeOf === 'function') {
|
|
295
|
+
const id = registerFunction(arg, owner, options);
|
|
296
|
+
return options.persistent ? { [TYPE_KEY]: 'fn', id, persistent: true } : { [TYPE_KEY]: 'fn', id };
|
|
253
297
|
}
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const result = {};
|
|
260
|
-
for (const key of Object.keys(arg)) {
|
|
261
|
-
setOwnProperty$1(result, key, deserialize0(source[key], generateCallback, getRemoteCallback, options));
|
|
298
|
+
if (typeOf === 'symbol')
|
|
299
|
+
return { [TYPE_KEY]: 'sym', v: serializeSymbol(arg) };
|
|
300
|
+
if (typeOf === 'object') {
|
|
301
|
+
if (seen.has(arg)) {
|
|
302
|
+
throw new TypeError('Cannot serialize circular structure');
|
|
262
303
|
}
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
return arg;
|
|
266
|
-
}
|
|
267
|
-
function deserializePrimitive(value, generateCallback, getRemoteCallback, options = {}) {
|
|
268
|
-
const undefinedPrefix = '$undefined$';
|
|
269
|
-
const nullPrefix = '$null$';
|
|
270
|
-
const errorPrefix = '$error$';
|
|
271
|
-
const functionPrefix = '$function$';
|
|
272
|
-
if (value === undefinedPrefix)
|
|
273
|
-
return undefined;
|
|
274
|
-
if (value === nullPrefix)
|
|
275
|
-
return null;
|
|
276
|
-
if (value.startsWith(errorPrefix)) {
|
|
277
|
-
let serialized;
|
|
304
|
+
seen.add(arg);
|
|
278
305
|
try {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
306
|
+
if (arg instanceof Error)
|
|
307
|
+
return serializeError(arg, owner, registerFunction, options, seen);
|
|
308
|
+
if (arg instanceof Date)
|
|
309
|
+
return { [TYPE_KEY]: 'date', v: arg.getTime() };
|
|
310
|
+
if (arg instanceof RegExp)
|
|
311
|
+
return { [TYPE_KEY]: 're', source: arg.source, flags: arg.flags };
|
|
312
|
+
if (arg instanceof Map) {
|
|
313
|
+
const entries = [];
|
|
314
|
+
for (const [k, v] of arg.entries()) {
|
|
315
|
+
entries.push([
|
|
316
|
+
serialize0(k, arg, registerFunction, options, seen),
|
|
317
|
+
serialize0(v, arg, registerFunction, options, seen),
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
return { [TYPE_KEY]: 'map', entries };
|
|
293
321
|
}
|
|
294
|
-
|
|
295
|
-
|
|
322
|
+
if (arg instanceof Set) {
|
|
323
|
+
const values = [];
|
|
324
|
+
for (const v of arg.values()) {
|
|
325
|
+
values.push(serialize0(v, arg, registerFunction, options, seen));
|
|
326
|
+
}
|
|
327
|
+
return { [TYPE_KEY]: 'set', values };
|
|
296
328
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const [, type, raw] = match;
|
|
305
|
-
switch (type) {
|
|
306
|
-
case 'number':
|
|
307
|
-
return Number(raw);
|
|
308
|
-
case 'string':
|
|
309
|
-
return raw;
|
|
310
|
-
case 'boolean':
|
|
311
|
-
return raw === 'true';
|
|
312
|
-
case 'bigint':
|
|
313
|
-
return BigInt(raw);
|
|
314
|
-
case 'symbol':
|
|
315
|
-
return deserializeSymbol(raw);
|
|
329
|
+
if (Array.isArray(arg)) {
|
|
330
|
+
return arg.map((item) => serialize0(item, arg, registerFunction, options, seen));
|
|
331
|
+
}
|
|
332
|
+
return serializePlainObject(arg, registerFunction, options, seen);
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
seen.delete(arg);
|
|
316
336
|
}
|
|
317
337
|
}
|
|
318
|
-
return
|
|
338
|
+
return null;
|
|
319
339
|
}
|
|
320
|
-
function
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
340
|
+
function serializeError(err, owner, registerFunction, options, seen) {
|
|
341
|
+
const result = { [TYPE_KEY]: 'err', message: err.message };
|
|
342
|
+
if (err.name && err.name !== 'Error')
|
|
343
|
+
result.name = err.name;
|
|
344
|
+
if (err.stack !== undefined)
|
|
345
|
+
result.stack = err.stack;
|
|
346
|
+
const cause = err.cause;
|
|
347
|
+
if (cause !== undefined) {
|
|
348
|
+
result.cause = serialize0(cause, owner, registerFunction, options, seen);
|
|
349
|
+
}
|
|
350
|
+
const RESERVED_ERROR_KEYS = new Set(['message', 'name', 'stack', 'cause']);
|
|
351
|
+
const extras = [];
|
|
352
|
+
for (const k of Reflect.ownKeys(err)) {
|
|
353
|
+
if (typeof k === 'symbol')
|
|
354
|
+
continue;
|
|
355
|
+
if (RESERVED_ERROR_KEYS.has(k))
|
|
356
|
+
continue;
|
|
357
|
+
const value = err[k];
|
|
358
|
+
extras.push([k, serialize0(value, owner, registerFunction, options, seen)]);
|
|
327
359
|
}
|
|
328
|
-
|
|
360
|
+
if (extras.length > 0)
|
|
361
|
+
result.extras = extras;
|
|
362
|
+
return result;
|
|
329
363
|
}
|
|
330
|
-
function
|
|
331
|
-
|
|
332
|
-
|
|
364
|
+
function serializePlainObject(source, registerFunction, options, seen) {
|
|
365
|
+
const record = source;
|
|
366
|
+
const ownKeys = Reflect.ownKeys(source).filter((k) => isEnumerable(source, k));
|
|
367
|
+
const symbolKeys = [];
|
|
368
|
+
const stringKeys = [];
|
|
369
|
+
for (const k of ownKeys) {
|
|
370
|
+
if (typeof k === 'symbol') {
|
|
371
|
+
if (isSerializableSymbol(k))
|
|
372
|
+
symbolKeys.push(k);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
stringKeys.push(k);
|
|
376
|
+
}
|
|
333
377
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
378
|
+
const hasReservedKey = Object.prototype.hasOwnProperty.call(record, TYPE_KEY);
|
|
379
|
+
if (symbolKeys.length > 0 || hasReservedKey) {
|
|
380
|
+
const entries = [];
|
|
381
|
+
for (const k of stringKeys) {
|
|
382
|
+
entries.push([k, serialize0(record[k], source, registerFunction, options, seen)]);
|
|
383
|
+
}
|
|
384
|
+
for (const k of symbolKeys) {
|
|
385
|
+
entries.push([
|
|
386
|
+
{ [TYPE_KEY]: 'sym', v: serializeSymbol(k) },
|
|
387
|
+
serialize0(record[k], source, registerFunction, options, seen),
|
|
388
|
+
]);
|
|
389
|
+
}
|
|
390
|
+
return { [TYPE_KEY]: 'obj', entries };
|
|
338
391
|
}
|
|
339
|
-
|
|
392
|
+
const result = {};
|
|
393
|
+
for (const k of stringKeys) {
|
|
394
|
+
setOwnProperty$1(result, k, serialize0(record[k], source, registerFunction, options, seen));
|
|
395
|
+
}
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
function isSerializableSymbol(value) {
|
|
399
|
+
if (Symbol.keyFor(value) !== undefined)
|
|
400
|
+
return true;
|
|
401
|
+
return getWellKnownSymbolName(value) !== undefined;
|
|
402
|
+
}
|
|
403
|
+
function isEnumerable(target, key) {
|
|
404
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
405
|
+
return descriptor ? descriptor.enumerable === true : false;
|
|
406
|
+
}
|
|
407
|
+
function serializeNumber(value) {
|
|
408
|
+
if (Number.isNaN(value))
|
|
409
|
+
return { [TYPE_KEY]: 'num', v: 'NaN' };
|
|
410
|
+
if (value === Infinity)
|
|
411
|
+
return { [TYPE_KEY]: 'num', v: 'Infinity' };
|
|
412
|
+
if (value === -Infinity)
|
|
413
|
+
return { [TYPE_KEY]: 'num', v: '-Infinity' };
|
|
414
|
+
if (Object.is(value, -0))
|
|
415
|
+
return { [TYPE_KEY]: 'num', v: '-0' };
|
|
416
|
+
return value;
|
|
340
417
|
}
|
|
341
418
|
function setOwnProperty$1(target, key, value) {
|
|
342
419
|
Object.defineProperty(target, key, {
|
|
@@ -346,58 +423,244 @@ function setOwnProperty$1(target, key, value) {
|
|
|
346
423
|
writable: true,
|
|
347
424
|
});
|
|
348
425
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const last = path[path.length - 1];
|
|
358
|
-
return typeof last === 'string' && /^on[A-Z]/.test(last);
|
|
426
|
+
function serializeSymbol(value) {
|
|
427
|
+
const globalKey = Symbol.keyFor(value);
|
|
428
|
+
if (globalKey)
|
|
429
|
+
return `global:${globalKey}`;
|
|
430
|
+
const wellKnownName = getWellKnownSymbolName(value);
|
|
431
|
+
if (wellKnownName)
|
|
432
|
+
return `wellKnown:${wellKnownName}`;
|
|
433
|
+
throw new TypeError('Cannot serialize non-global symbol');
|
|
359
434
|
}
|
|
360
435
|
|
|
361
|
-
|
|
362
|
-
|
|
436
|
+
const ERROR_CONSTRUCTORS = {
|
|
437
|
+
Error,
|
|
438
|
+
TypeError: TypeError,
|
|
439
|
+
RangeError: RangeError,
|
|
440
|
+
SyntaxError: SyntaxError,
|
|
441
|
+
ReferenceError: ReferenceError,
|
|
442
|
+
URIError: URIError,
|
|
443
|
+
EvalError: EvalError,
|
|
444
|
+
};
|
|
445
|
+
function deserialize(arg, generateCallback, getRemoteCallback) {
|
|
446
|
+
return deserialize0(arg, generateCallback, getRemoteCallback);
|
|
363
447
|
}
|
|
364
|
-
function
|
|
365
|
-
if (arg === undefined) {
|
|
366
|
-
return '$undefined$';
|
|
367
|
-
}
|
|
448
|
+
function deserialize0(arg, generateCallback, getRemoteCallback) {
|
|
368
449
|
if (arg === null)
|
|
369
|
-
return
|
|
370
|
-
if (typeof arg
|
|
371
|
-
|
|
372
|
-
|
|
450
|
+
return null;
|
|
451
|
+
if (typeof arg !== 'object')
|
|
452
|
+
return arg;
|
|
453
|
+
if (Array.isArray(arg)) {
|
|
454
|
+
return arg.map((item) => deserialize0(item, generateCallback, getRemoteCallback));
|
|
455
|
+
}
|
|
456
|
+
const source = arg;
|
|
457
|
+
const tag = source[TYPE_KEY];
|
|
458
|
+
if (typeof tag === 'string') {
|
|
459
|
+
return deserializeTagged(tag, source, generateCallback, getRemoteCallback);
|
|
460
|
+
}
|
|
461
|
+
const result = {};
|
|
462
|
+
for (const key of Object.keys(source)) {
|
|
463
|
+
setOwnProperty(result, key, deserialize0(source[key], generateCallback, getRemoteCallback));
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
function deserializeTagged(tag, source, generateCallback, getRemoteCallback) {
|
|
468
|
+
switch (tag) {
|
|
469
|
+
case 'undef':
|
|
470
|
+
return undefined;
|
|
471
|
+
case 'big':
|
|
472
|
+
return deserializeBigInt(source.v);
|
|
473
|
+
case 'sym':
|
|
474
|
+
return deserializeSymbol(expectString(source.v, 'symbol'));
|
|
475
|
+
case 'num':
|
|
476
|
+
return deserializeNumber(expectString(source.v, 'number'));
|
|
477
|
+
case 'err':
|
|
478
|
+
return deserializeError(source, generateCallback, getRemoteCallback);
|
|
479
|
+
case 'date':
|
|
480
|
+
return deserializeDate(source.v);
|
|
481
|
+
case 're':
|
|
482
|
+
return deserializeRegExp(source.source, source.flags);
|
|
483
|
+
case 'map':
|
|
484
|
+
return deserializeMap(source.entries, generateCallback, getRemoteCallback);
|
|
485
|
+
case 'set':
|
|
486
|
+
return deserializeSet(source.values, generateCallback, getRemoteCallback);
|
|
487
|
+
case 'fn':
|
|
488
|
+
return deserializeFunction(source, generateCallback, getRemoteCallback);
|
|
489
|
+
case 'obj':
|
|
490
|
+
return deserializeWrappedObject(source.entries, generateCallback, getRemoteCallback);
|
|
491
|
+
default: {
|
|
492
|
+
const result = {};
|
|
493
|
+
for (const key of Object.keys(source)) {
|
|
494
|
+
setOwnProperty(result, key, deserialize0(source[key], generateCallback, getRemoteCallback));
|
|
495
|
+
}
|
|
496
|
+
return result;
|
|
373
497
|
}
|
|
374
|
-
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function deserializeBigInt(raw) {
|
|
501
|
+
if (typeof raw !== 'string')
|
|
502
|
+
throw new TypeError('Invalid bigint payload');
|
|
503
|
+
try {
|
|
504
|
+
return BigInt(raw);
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
throw new TypeError(`Invalid bigint payload: ${raw}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function deserializeDate(raw) {
|
|
511
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
|
|
512
|
+
throw new TypeError('Invalid date payload');
|
|
513
|
+
}
|
|
514
|
+
return new Date(raw);
|
|
515
|
+
}
|
|
516
|
+
function deserializeRegExp(rawSource, rawFlags) {
|
|
517
|
+
if (typeof rawSource !== 'string')
|
|
518
|
+
throw new TypeError('Invalid regexp source');
|
|
519
|
+
if (typeof rawFlags !== 'string')
|
|
520
|
+
throw new TypeError('Invalid regexp flags');
|
|
521
|
+
try {
|
|
522
|
+
return new RegExp(rawSource, rawFlags);
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
throw new TypeError(`Invalid regexp payload: ${err instanceof Error ? err.message : String(err)}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function deserializeMap(entries, generateCallback, getRemoteCallback) {
|
|
529
|
+
const result = new Map();
|
|
530
|
+
if (!Array.isArray(entries))
|
|
531
|
+
return result;
|
|
532
|
+
for (const entry of entries) {
|
|
533
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
534
|
+
continue;
|
|
535
|
+
const k = deserialize0(entry[0], generateCallback, getRemoteCallback);
|
|
536
|
+
const v = deserialize0(entry[1], generateCallback, getRemoteCallback);
|
|
537
|
+
result.set(k, v);
|
|
538
|
+
}
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
function deserializeSet(values, generateCallback, getRemoteCallback) {
|
|
542
|
+
const result = new Set();
|
|
543
|
+
if (!Array.isArray(values))
|
|
544
|
+
return result;
|
|
545
|
+
for (const v of values) {
|
|
546
|
+
result.add(deserialize0(v, generateCallback, getRemoteCallback));
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
function deserializeFunction(source, generateCallback, getRemoteCallback) {
|
|
551
|
+
const id = source.id;
|
|
552
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
553
|
+
throw new TypeError('Invalid function payload');
|
|
554
|
+
}
|
|
555
|
+
const persistent = source.persistent === true;
|
|
556
|
+
const fnOptions = { persistent };
|
|
557
|
+
const invoke = (args) => {
|
|
558
|
+
if (persistent)
|
|
559
|
+
return generateCallback(id, args, fnOptions);
|
|
560
|
+
return generateCallback(id, args);
|
|
561
|
+
};
|
|
562
|
+
return getRemoteCallback ? getRemoteCallback(id, invoke, fnOptions) : (...args) => invoke(args);
|
|
563
|
+
}
|
|
564
|
+
function deserializeError(source, generateCallback, getRemoteCallback) {
|
|
565
|
+
const message = typeof source.message === 'string' ? source.message : '';
|
|
566
|
+
const name = typeof source.name === 'string' ? source.name : 'Error';
|
|
567
|
+
const Ctor = ERROR_CONSTRUCTORS[name] ?? Error;
|
|
568
|
+
const error = new Ctor(message);
|
|
569
|
+
if (name && name !== Ctor.name) {
|
|
375
570
|
try {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
571
|
+
error.name = name;
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
// ignore non-writable name
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (typeof source.stack === 'string')
|
|
578
|
+
error.stack = source.stack;
|
|
579
|
+
if (source.cause !== undefined) {
|
|
580
|
+
error.cause = deserialize0(source.cause, generateCallback, getRemoteCallback);
|
|
581
|
+
}
|
|
582
|
+
if (Array.isArray(source.extras)) {
|
|
583
|
+
for (const entry of source.extras) {
|
|
584
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
585
|
+
continue;
|
|
586
|
+
const [rawKey, rawValue] = entry;
|
|
587
|
+
if (typeof rawKey !== 'string')
|
|
588
|
+
continue;
|
|
589
|
+
try {
|
|
590
|
+
error[rawKey] = deserialize0(rawValue, generateCallback, getRemoteCallback);
|
|
381
591
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
for (const key of Object.keys(arg)) {
|
|
385
|
-
setOwnProperty(result, key, serialize0(source[key], registerFunction, options, seen));
|
|
592
|
+
catch {
|
|
593
|
+
// ignore non-writable property
|
|
386
594
|
}
|
|
387
|
-
return result;
|
|
388
595
|
}
|
|
389
|
-
|
|
390
|
-
|
|
596
|
+
}
|
|
597
|
+
return error;
|
|
598
|
+
}
|
|
599
|
+
function deserializeWrappedObject(entries, generateCallback, getRemoteCallback) {
|
|
600
|
+
const result = {};
|
|
601
|
+
if (!Array.isArray(entries))
|
|
602
|
+
return result;
|
|
603
|
+
for (const entry of entries) {
|
|
604
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
605
|
+
continue;
|
|
606
|
+
const rawKey = entry[0];
|
|
607
|
+
const value = deserialize0(entry[1], generateCallback, getRemoteCallback);
|
|
608
|
+
const key = resolveObjectKey(rawKey);
|
|
609
|
+
if (key === undefined) {
|
|
610
|
+
console.warn('chrome-in-iframe: dropping wrapped-object entry with unresolvable key', rawKey);
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
Object.defineProperty(result, key, {
|
|
614
|
+
value,
|
|
615
|
+
enumerable: true,
|
|
616
|
+
configurable: true,
|
|
617
|
+
writable: true,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
function resolveObjectKey(raw) {
|
|
623
|
+
if (typeof raw === 'string')
|
|
624
|
+
return raw;
|
|
625
|
+
if (raw !== null && typeof raw === 'object') {
|
|
626
|
+
const tagged = raw;
|
|
627
|
+
if (tagged[TYPE_KEY] === 'sym' && typeof tagged.v === 'string') {
|
|
628
|
+
try {
|
|
629
|
+
return deserializeSymbol(tagged.v);
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|
|
391
634
|
}
|
|
392
635
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
636
|
+
return undefined;
|
|
637
|
+
}
|
|
638
|
+
function expectString(raw, label) {
|
|
639
|
+
if (typeof raw !== 'string')
|
|
640
|
+
throw new TypeError(`Invalid ${label} payload`);
|
|
641
|
+
return raw;
|
|
642
|
+
}
|
|
643
|
+
function deserializeNumber(raw) {
|
|
644
|
+
if (raw === 'NaN')
|
|
645
|
+
return NaN;
|
|
646
|
+
if (raw === 'Infinity')
|
|
647
|
+
return Infinity;
|
|
648
|
+
if (raw === '-Infinity')
|
|
649
|
+
return -Infinity;
|
|
650
|
+
if (raw === '-0')
|
|
651
|
+
return -0;
|
|
652
|
+
return Number(raw);
|
|
653
|
+
}
|
|
654
|
+
function deserializeSymbol(value) {
|
|
655
|
+
if (value.startsWith('global:')) {
|
|
656
|
+
return Symbol.for(value.slice('global:'.length));
|
|
396
657
|
}
|
|
397
|
-
if (
|
|
398
|
-
|
|
658
|
+
if (value.startsWith('wellKnown:')) {
|
|
659
|
+
const symbol = getWellKnownSymbol(value.slice('wellKnown:'.length));
|
|
660
|
+
if (symbol)
|
|
661
|
+
return symbol;
|
|
399
662
|
}
|
|
400
|
-
|
|
663
|
+
throw new TypeError('Cannot deserialize symbol');
|
|
401
664
|
}
|
|
402
665
|
function setOwnProperty(target, key, value) {
|
|
403
666
|
Object.defineProperty(target, key, {
|
|
@@ -407,14 +670,18 @@ function setOwnProperty(target, key, value) {
|
|
|
407
670
|
writable: true,
|
|
408
671
|
});
|
|
409
672
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
673
|
+
|
|
674
|
+
function isListenerRegistrationPath(path) {
|
|
675
|
+
const last = path[path.length - 1];
|
|
676
|
+
return last === 'addListener' || last === 'hasListener';
|
|
677
|
+
}
|
|
678
|
+
function isListenerRemovalPath(path) {
|
|
679
|
+
const last = path[path.length - 1];
|
|
680
|
+
return last === 'removeListener';
|
|
681
|
+
}
|
|
682
|
+
function isLikelyListenerPath(path) {
|
|
683
|
+
const last = path[path.length - 1];
|
|
684
|
+
return typeof last === 'string' && /^on[A-Z]/.test(last);
|
|
418
685
|
}
|
|
419
686
|
|
|
420
687
|
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
@@ -427,48 +694,68 @@ function readProperty(target, key) {
|
|
|
427
694
|
return target[key];
|
|
428
695
|
}
|
|
429
696
|
|
|
430
|
-
|
|
697
|
+
const boundCache = new WeakMap();
|
|
698
|
+
function bindMethod(fn, owner) {
|
|
699
|
+
if (owner === null || typeof owner !== 'object') {
|
|
700
|
+
return (...args) => Reflect.apply(fn, owner, args);
|
|
701
|
+
}
|
|
702
|
+
let perOwner = boundCache.get(fn);
|
|
703
|
+
if (!perOwner) {
|
|
704
|
+
perOwner = new WeakMap();
|
|
705
|
+
boundCache.set(fn, perOwner);
|
|
706
|
+
}
|
|
707
|
+
const cached = perOwner.get(owner);
|
|
708
|
+
if (cached)
|
|
709
|
+
return cached;
|
|
710
|
+
const bound = (...args) => Reflect.apply(fn, owner, args);
|
|
711
|
+
perOwner.set(owner, bound);
|
|
712
|
+
return bound;
|
|
713
|
+
}
|
|
714
|
+
function handleAccessPropertyRequest(data, ctx, meta) {
|
|
431
715
|
const target = ctx.getDelegateTarget();
|
|
432
716
|
const channel = ctx.getMessageChannel();
|
|
433
717
|
if (data.path.length === 0) {
|
|
434
718
|
channel.getSender().sendMessage('accessPropertyResponse', {
|
|
435
719
|
id: data.id,
|
|
436
720
|
error: { message: 'Property path must not be empty' },
|
|
437
|
-
});
|
|
721
|
+
}, meta.senderInstanceId);
|
|
438
722
|
return;
|
|
439
723
|
}
|
|
440
724
|
if (!target) {
|
|
441
725
|
channel.getSender().sendMessage('accessPropertyResponse', {
|
|
442
726
|
id: data.id,
|
|
443
727
|
error: { message: 'No delegate target is configured' },
|
|
444
|
-
});
|
|
728
|
+
}, meta.senderInstanceId);
|
|
445
729
|
return;
|
|
446
730
|
}
|
|
447
731
|
try {
|
|
448
732
|
let current = target;
|
|
733
|
+
let owner = target;
|
|
449
734
|
for (const key of data.path) {
|
|
450
735
|
if (current === undefined || current === null) {
|
|
451
736
|
channel.getSender().sendMessage('accessPropertyResponse', {
|
|
452
737
|
id: data.id,
|
|
453
738
|
data: serialize(current, ctx.registerFunction),
|
|
454
|
-
});
|
|
739
|
+
}, meta.senderInstanceId);
|
|
455
740
|
return;
|
|
456
741
|
}
|
|
742
|
+
owner = current;
|
|
457
743
|
current = readProperty(current, key);
|
|
458
744
|
}
|
|
745
|
+
const value = typeof current === 'function' ? bindMethod(current, owner) : current;
|
|
459
746
|
channel.getSender().sendMessage('accessPropertyResponse', {
|
|
460
747
|
id: data.id,
|
|
461
|
-
data: serialize(
|
|
462
|
-
});
|
|
748
|
+
data: serialize(value, ctx.registerFunction, { persistent: isLikelyListenerPath(data.path) }),
|
|
749
|
+
}, meta.senderInstanceId);
|
|
463
750
|
}
|
|
464
751
|
catch (err) {
|
|
465
752
|
channel.getSender().sendMessage('accessPropertyResponse', {
|
|
466
753
|
id: data.id,
|
|
467
754
|
error: serializeThrownError(err),
|
|
468
|
-
});
|
|
755
|
+
}, meta.senderInstanceId);
|
|
469
756
|
}
|
|
470
757
|
}
|
|
471
|
-
function handleAccessPropertyResponse(data, ctx) {
|
|
758
|
+
function handleAccessPropertyResponse(data, ctx, meta) {
|
|
472
759
|
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
473
760
|
if (!pending)
|
|
474
761
|
return;
|
|
@@ -478,19 +765,29 @@ function handleAccessPropertyResponse(data, ctx) {
|
|
|
478
765
|
pending.reject(err);
|
|
479
766
|
return;
|
|
480
767
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
768
|
+
const scopedRemoteCallback = createScopedRemoteCallback$2(ctx, meta.senderInstanceId);
|
|
769
|
+
try {
|
|
770
|
+
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
771
|
+
return ctx.invokeFunctionById(id, args, options);
|
|
772
|
+
}, scopedRemoteCallback));
|
|
773
|
+
}
|
|
774
|
+
catch (err) {
|
|
775
|
+
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function createScopedRemoteCallback$2(ctx, remoteInstanceId) {
|
|
779
|
+
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
484
780
|
}
|
|
485
781
|
|
|
486
|
-
function handleInvokeRequest(data, ctx) {
|
|
782
|
+
function handleInvokeRequest(data, ctx, meta) {
|
|
487
783
|
const target = ctx.getDelegateTarget();
|
|
488
784
|
const channel = ctx.getMessageChannel();
|
|
785
|
+
const scopedRemoteCallback = createScopedRemoteCallback$1(ctx, meta.senderInstanceId);
|
|
489
786
|
if (!target) {
|
|
490
787
|
channel.getSender().sendMessage('invokeResponse', {
|
|
491
788
|
id: data.id,
|
|
492
789
|
error: { message: 'No delegate target is configured' },
|
|
493
|
-
});
|
|
790
|
+
}, meta.senderInstanceId);
|
|
494
791
|
return;
|
|
495
792
|
}
|
|
496
793
|
let current = target;
|
|
@@ -502,7 +799,7 @@ function handleInvokeRequest(data, ctx) {
|
|
|
502
799
|
error: {
|
|
503
800
|
message: `Cannot read property '${String(data.path[i + 1])}' of ${String(data.path[i])}`,
|
|
504
801
|
},
|
|
505
|
-
});
|
|
802
|
+
}, meta.senderInstanceId);
|
|
506
803
|
return;
|
|
507
804
|
}
|
|
508
805
|
}
|
|
@@ -512,12 +809,22 @@ function handleInvokeRequest(data, ctx) {
|
|
|
512
809
|
channel.getSender().sendMessage('invokeResponse', {
|
|
513
810
|
id: data.id,
|
|
514
811
|
error: { message: `'${String(lastKey)}' is not a function` },
|
|
515
|
-
});
|
|
812
|
+
}, meta.senderInstanceId);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
let deserializedArgs;
|
|
816
|
+
try {
|
|
817
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, (id, args, options) => {
|
|
818
|
+
return ctx.invokeFunctionById(id, args, options);
|
|
819
|
+
}, scopedRemoteCallback));
|
|
820
|
+
}
|
|
821
|
+
catch (err) {
|
|
822
|
+
channel.getSender().sendMessage('invokeResponse', {
|
|
823
|
+
id: data.id,
|
|
824
|
+
error: serializeThrownError(err),
|
|
825
|
+
}, meta.senderInstanceId);
|
|
516
826
|
return;
|
|
517
827
|
}
|
|
518
|
-
const deserializedArgs = data.args.map((arg) => deserialize(arg, (id, args, options) => {
|
|
519
|
-
return ctx.invokeFunctionById(id, args, options);
|
|
520
|
-
}, ctx.getRemoteCallback, { persistent: isListenerRegistrationPath(data.path) }));
|
|
521
828
|
let result;
|
|
522
829
|
try {
|
|
523
830
|
result = Reflect.apply(fn, current, deserializedArgs);
|
|
@@ -526,26 +833,26 @@ function handleInvokeRequest(data, ctx) {
|
|
|
526
833
|
channel.getSender().sendMessage('invokeResponse', {
|
|
527
834
|
id: data.id,
|
|
528
835
|
error: serializeThrownError(err),
|
|
529
|
-
});
|
|
836
|
+
}, meta.senderInstanceId);
|
|
530
837
|
return;
|
|
531
838
|
}
|
|
532
839
|
if (result instanceof Promise) {
|
|
533
840
|
result
|
|
534
841
|
.then((value) => {
|
|
535
|
-
sendInvokeSuccess(data.id, value, ctx);
|
|
842
|
+
sendInvokeSuccess(data.id, value, ctx, meta.senderInstanceId);
|
|
536
843
|
})
|
|
537
844
|
.catch((err) => {
|
|
538
845
|
channel.getSender().sendMessage('invokeResponse', {
|
|
539
846
|
id: data.id,
|
|
540
847
|
error: serializeThrownError(err),
|
|
541
|
-
});
|
|
848
|
+
}, meta.senderInstanceId);
|
|
542
849
|
});
|
|
543
850
|
}
|
|
544
851
|
else {
|
|
545
|
-
sendInvokeSuccess(data.id, result, ctx);
|
|
852
|
+
sendInvokeSuccess(data.id, result, ctx, meta.senderInstanceId);
|
|
546
853
|
}
|
|
547
854
|
}
|
|
548
|
-
function handleInvokeResponse(data, ctx) {
|
|
855
|
+
function handleInvokeResponse(data, ctx, meta) {
|
|
549
856
|
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
550
857
|
if (!pending)
|
|
551
858
|
return;
|
|
@@ -555,62 +862,79 @@ function handleInvokeResponse(data, ctx) {
|
|
|
555
862
|
pending.reject(err);
|
|
556
863
|
}
|
|
557
864
|
else {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
865
|
+
const scopedRemoteCallback = createScopedRemoteCallback$1(ctx, meta.senderInstanceId);
|
|
866
|
+
try {
|
|
867
|
+
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
868
|
+
return ctx.invokeFunctionById(id, args, options);
|
|
869
|
+
}, scopedRemoteCallback));
|
|
870
|
+
}
|
|
871
|
+
catch (err) {
|
|
872
|
+
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
873
|
+
}
|
|
561
874
|
}
|
|
562
875
|
}
|
|
563
|
-
function sendInvokeSuccess(id, value, ctx) {
|
|
876
|
+
function sendInvokeSuccess(id, value, ctx, targetInstanceId) {
|
|
564
877
|
const channel = ctx.getMessageChannel();
|
|
565
878
|
try {
|
|
566
879
|
channel.getSender().sendMessage('invokeResponse', {
|
|
567
880
|
id,
|
|
568
881
|
data: serialize(value, ctx.registerFunction),
|
|
569
|
-
});
|
|
882
|
+
}, targetInstanceId);
|
|
570
883
|
}
|
|
571
884
|
catch (err) {
|
|
572
885
|
channel.getSender().sendMessage('invokeResponse', {
|
|
573
886
|
id,
|
|
574
887
|
error: serializeThrownError(err),
|
|
575
|
-
});
|
|
888
|
+
}, targetInstanceId);
|
|
576
889
|
}
|
|
577
890
|
}
|
|
891
|
+
function createScopedRemoteCallback$1(ctx, remoteInstanceId) {
|
|
892
|
+
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
893
|
+
}
|
|
578
894
|
|
|
579
|
-
function handleCallbackInvoke(data, ctx) {
|
|
895
|
+
function handleCallbackInvoke(data, ctx, meta) {
|
|
580
896
|
const channel = ctx.getMessageChannel();
|
|
581
|
-
const
|
|
582
|
-
const
|
|
583
|
-
if (!
|
|
897
|
+
const persistentEntry = ctx.getPersistentFunctionCache().get(data.id);
|
|
898
|
+
const entry = persistentEntry ?? ctx.getFunctionCache().get(data.id);
|
|
899
|
+
if (!entry) {
|
|
584
900
|
const error = { message: `Callback '${data.id}' is not available or has expired` };
|
|
585
901
|
console.warn(`chrome-in-iframe: ${error.message}`);
|
|
586
902
|
channel.getSender().sendMessage('invokeFunctionByIdResponse', {
|
|
587
903
|
id: data.callId,
|
|
588
904
|
error,
|
|
589
|
-
});
|
|
905
|
+
}, meta.senderInstanceId);
|
|
590
906
|
return;
|
|
591
907
|
}
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
}, ctx.getRemoteCallback, { persistent: isPersistentFunction }));
|
|
908
|
+
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
909
|
+
let deserializedArgs;
|
|
595
910
|
try {
|
|
596
|
-
|
|
911
|
+
deserializedArgs = data.args.map((arg) => deserialize(arg, (id, args, options) => {
|
|
912
|
+
return ctx.invokeFunctionById(id, args, options);
|
|
913
|
+
}, scopedRemoteCallback));
|
|
914
|
+
}
|
|
915
|
+
catch (err) {
|
|
916
|
+
sendCallbackError(data.callId, err, ctx, meta.senderInstanceId);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
const result = Reflect.apply(entry.fn, entry.thisArg, deserializedArgs);
|
|
597
921
|
if (result instanceof Promise) {
|
|
598
922
|
result
|
|
599
923
|
.then((value) => {
|
|
600
|
-
sendCallbackSuccess(data.callId, value, ctx);
|
|
924
|
+
sendCallbackSuccess(data.callId, value, ctx, meta.senderInstanceId);
|
|
601
925
|
})
|
|
602
926
|
.catch((err) => {
|
|
603
|
-
sendCallbackError(data.callId, err, ctx);
|
|
927
|
+
sendCallbackError(data.callId, err, ctx, meta.senderInstanceId);
|
|
604
928
|
});
|
|
605
929
|
return;
|
|
606
930
|
}
|
|
607
|
-
sendCallbackSuccess(data.callId, result, ctx);
|
|
931
|
+
sendCallbackSuccess(data.callId, result, ctx, meta.senderInstanceId);
|
|
608
932
|
}
|
|
609
933
|
catch (err) {
|
|
610
|
-
sendCallbackError(data.callId, err, ctx);
|
|
934
|
+
sendCallbackError(data.callId, err, ctx, meta.senderInstanceId);
|
|
611
935
|
}
|
|
612
936
|
}
|
|
613
|
-
function handleCallbackInvokeResponse(data, ctx) {
|
|
937
|
+
function handleCallbackInvokeResponse(data, ctx, meta) {
|
|
614
938
|
const pending = ctx.getAndRemovePendingPromise(data.id);
|
|
615
939
|
if (!pending)
|
|
616
940
|
return;
|
|
@@ -620,30 +944,52 @@ function handleCallbackInvokeResponse(data, ctx) {
|
|
|
620
944
|
pending.reject(err);
|
|
621
945
|
return;
|
|
622
946
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
947
|
+
const scopedRemoteCallback = createScopedRemoteCallback(ctx, meta.senderInstanceId);
|
|
948
|
+
try {
|
|
949
|
+
pending.resolve(deserialize(data.data, (id, args, options) => {
|
|
950
|
+
return ctx.invokeFunctionById(id, args, options);
|
|
951
|
+
}, scopedRemoteCallback));
|
|
952
|
+
}
|
|
953
|
+
catch (err) {
|
|
954
|
+
pending.reject(err instanceof Error ? err : new Error(String(err)));
|
|
955
|
+
}
|
|
626
956
|
}
|
|
627
|
-
function sendCallbackSuccess(id, value, ctx) {
|
|
957
|
+
function sendCallbackSuccess(id, value, ctx, targetInstanceId) {
|
|
628
958
|
const channel = ctx.getMessageChannel();
|
|
629
959
|
try {
|
|
630
960
|
channel.getSender().sendMessage('invokeFunctionByIdResponse', {
|
|
631
961
|
id,
|
|
632
962
|
data: serialize(value, ctx.registerFunction),
|
|
633
|
-
});
|
|
963
|
+
}, targetInstanceId);
|
|
634
964
|
}
|
|
635
965
|
catch (err) {
|
|
636
|
-
sendCallbackError(id, err, ctx);
|
|
966
|
+
sendCallbackError(id, err, ctx, targetInstanceId);
|
|
637
967
|
}
|
|
638
968
|
}
|
|
639
|
-
function sendCallbackError(id, err, ctx) {
|
|
969
|
+
function sendCallbackError(id, err, ctx, targetInstanceId) {
|
|
640
970
|
ctx
|
|
641
971
|
.getMessageChannel()
|
|
642
972
|
.getSender()
|
|
643
973
|
.sendMessage('invokeFunctionByIdResponse', {
|
|
644
974
|
id,
|
|
645
975
|
error: serializeThrownError(err),
|
|
646
|
-
});
|
|
976
|
+
}, targetInstanceId);
|
|
977
|
+
}
|
|
978
|
+
function createScopedRemoteCallback(ctx, remoteInstanceId) {
|
|
979
|
+
return (id, invoke, options) => ctx.getRemoteCallback(id, invoke, options, remoteInstanceId);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const MAX_RELEASE_BATCH = 10000;
|
|
983
|
+
function handleReleaseCallbacks(data, ctx, _meta) {
|
|
984
|
+
if (data.ids.length > MAX_RELEASE_BATCH)
|
|
985
|
+
return;
|
|
986
|
+
for (const id of data.ids) {
|
|
987
|
+
ctx.getFunctionCache().delete(id);
|
|
988
|
+
ctx.getPersistentFunctionCache().delete(id);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
function handleDestroyEndpoint(_data, ctx, meta) {
|
|
992
|
+
ctx.handleRemoteDestroy(meta.senderInstanceId);
|
|
647
993
|
}
|
|
648
994
|
|
|
649
995
|
function createProcessorRegistry() {
|
|
@@ -659,56 +1005,90 @@ function createProcessorRegistry() {
|
|
|
659
1005
|
}
|
|
660
1006
|
|
|
661
1007
|
const DEFAULT_TIMEOUT = 30000;
|
|
662
|
-
const
|
|
663
|
-
const
|
|
664
|
-
const
|
|
665
|
-
const
|
|
666
|
-
function createClientContext(getMessageChannel, options = {}) {
|
|
1008
|
+
const DEFAULT_FUNCTION_CACHE_MAX = 100;
|
|
1009
|
+
const DEFAULT_FUNCTION_CACHE_TTL = 5 * 60 * 1000;
|
|
1010
|
+
const DEFAULT_REMOTE_CALLBACK_CACHE_MAX = 500;
|
|
1011
|
+
const DEFAULT_REMOTE_CALLBACK_CACHE_TTL = 5 * 60 * 1000;
|
|
1012
|
+
function createClientContext(getMessageChannel, instanceId, options = {}) {
|
|
1013
|
+
const remoteCacheMax = options.remoteCallbackCacheMax ?? DEFAULT_REMOTE_CALLBACK_CACHE_MAX;
|
|
1014
|
+
const remoteCacheTtl = options.remoteCallbackCacheTtl ?? DEFAULT_REMOTE_CALLBACK_CACHE_TTL;
|
|
667
1015
|
const pending = new Map();
|
|
1016
|
+
const functionIds = new Map();
|
|
668
1017
|
const functionCache = new LRUCache({
|
|
669
|
-
max:
|
|
670
|
-
ttl:
|
|
1018
|
+
max: options.functionCacheMax ?? DEFAULT_FUNCTION_CACHE_MAX,
|
|
1019
|
+
ttl: options.functionCacheTtl ?? DEFAULT_FUNCTION_CACHE_TTL,
|
|
1020
|
+
dispose: (value, _key, reason) => {
|
|
1021
|
+
if (reason === 'set' || reason === 'delete')
|
|
1022
|
+
return;
|
|
1023
|
+
functionIds.delete(value.fn);
|
|
1024
|
+
},
|
|
671
1025
|
});
|
|
672
1026
|
const persistentFunctionCache = new Map();
|
|
673
|
-
const
|
|
674
|
-
const
|
|
675
|
-
max: REMOTE_CALLBACK_CACHE_MAX,
|
|
676
|
-
ttl: REMOTE_CALLBACK_CACHE_TTL,
|
|
677
|
-
});
|
|
678
|
-
const persistentRemoteCallbacks = new Map();
|
|
1027
|
+
const remoteCallbacksByOwner = new Map();
|
|
1028
|
+
const persistentRemoteCallbacksByOwner = new Map();
|
|
679
1029
|
let destroyed = false;
|
|
680
|
-
const
|
|
1030
|
+
const getOrCreateRemoteLru = (remoteId) => {
|
|
1031
|
+
let lru = remoteCallbacksByOwner.get(remoteId);
|
|
1032
|
+
if (!lru) {
|
|
1033
|
+
lru = new LRUCache({ max: remoteCacheMax, ttl: remoteCacheTtl });
|
|
1034
|
+
remoteCallbacksByOwner.set(remoteId, lru);
|
|
1035
|
+
}
|
|
1036
|
+
return lru;
|
|
1037
|
+
};
|
|
1038
|
+
const getOrCreatePersistentRemoteMap = (remoteId) => {
|
|
1039
|
+
let map = persistentRemoteCallbacksByOwner.get(remoteId);
|
|
1040
|
+
if (!map) {
|
|
1041
|
+
map = new Map();
|
|
1042
|
+
persistentRemoteCallbacksByOwner.set(remoteId, map);
|
|
1043
|
+
}
|
|
1044
|
+
return map;
|
|
1045
|
+
};
|
|
1046
|
+
const registerFunction = (fn, thisArg, callbackOptions = {}) => {
|
|
681
1047
|
let id = functionIds.get(fn);
|
|
682
1048
|
if (!id) {
|
|
683
1049
|
id = createId('cb');
|
|
684
1050
|
functionIds.set(fn, id);
|
|
685
1051
|
}
|
|
1052
|
+
const entry = { fn, thisArg };
|
|
686
1053
|
if (callbackOptions.persistent) {
|
|
687
|
-
persistentFunctionCache.set(id,
|
|
1054
|
+
persistentFunctionCache.set(id, entry);
|
|
688
1055
|
functionCache.delete(id);
|
|
689
1056
|
}
|
|
690
1057
|
else if (!persistentFunctionCache.has(id)) {
|
|
691
|
-
functionCache.set(id,
|
|
1058
|
+
functionCache.set(id, entry);
|
|
692
1059
|
}
|
|
693
1060
|
return id;
|
|
694
1061
|
};
|
|
695
|
-
const getRemoteCallback = (id, invoke, callbackOptions
|
|
696
|
-
|
|
1062
|
+
const getRemoteCallback = (id, invoke, callbackOptions, remoteInstanceId) => {
|
|
1063
|
+
const opts = callbackOptions ?? {};
|
|
1064
|
+
const lru = remoteCallbacksByOwner.get(remoteInstanceId);
|
|
1065
|
+
const pmap = persistentRemoteCallbacksByOwner.get(remoteInstanceId);
|
|
1066
|
+
let callback = pmap?.get(id) ?? lru?.get(id);
|
|
697
1067
|
if (!callback) {
|
|
698
1068
|
callback = (...args) => invoke(args);
|
|
699
|
-
if (
|
|
700
|
-
|
|
1069
|
+
if (opts.persistent) {
|
|
1070
|
+
getOrCreatePersistentRemoteMap(remoteInstanceId).set(id, callback);
|
|
701
1071
|
}
|
|
702
1072
|
else {
|
|
703
|
-
|
|
1073
|
+
getOrCreateRemoteLru(remoteInstanceId).set(id, callback);
|
|
704
1074
|
}
|
|
705
1075
|
}
|
|
706
|
-
else if (
|
|
707
|
-
|
|
708
|
-
|
|
1076
|
+
else if (opts.persistent && !pmap?.has(id)) {
|
|
1077
|
+
lru?.delete(id);
|
|
1078
|
+
getOrCreatePersistentRemoteMap(remoteInstanceId).set(id, callback);
|
|
709
1079
|
}
|
|
710
1080
|
return callback;
|
|
711
1081
|
};
|
|
1082
|
+
const notifyReleaseCallbacks = (ids) => {
|
|
1083
|
+
if (ids.length === 0)
|
|
1084
|
+
return;
|
|
1085
|
+
try {
|
|
1086
|
+
getMessageChannel().getSender().sendMessage('releaseCallbacks', { ids });
|
|
1087
|
+
}
|
|
1088
|
+
catch {
|
|
1089
|
+
// channel may not be available yet
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
712
1092
|
return {
|
|
713
1093
|
getDelegateTarget() {
|
|
714
1094
|
return options.delegateTarget;
|
|
@@ -716,17 +1096,20 @@ function createClientContext(getMessageChannel, options = {}) {
|
|
|
716
1096
|
getMessageChannel() {
|
|
717
1097
|
return getMessageChannel();
|
|
718
1098
|
},
|
|
1099
|
+
getInstanceId() {
|
|
1100
|
+
return instanceId;
|
|
1101
|
+
},
|
|
719
1102
|
getFunctionCache() {
|
|
720
1103
|
return functionCache;
|
|
721
1104
|
},
|
|
722
1105
|
getPersistentFunctionCache() {
|
|
723
1106
|
return persistentFunctionCache;
|
|
724
1107
|
},
|
|
725
|
-
registerFunction(fn, callbackOptions) {
|
|
726
|
-
return registerFunction(fn, callbackOptions);
|
|
1108
|
+
registerFunction(fn, thisArg, callbackOptions) {
|
|
1109
|
+
return registerFunction(fn, thisArg, callbackOptions);
|
|
727
1110
|
},
|
|
728
|
-
getRemoteCallback(id, invoke, callbackOptions) {
|
|
729
|
-
return getRemoteCallback(id, invoke, callbackOptions);
|
|
1111
|
+
getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId) {
|
|
1112
|
+
return getRemoteCallback(id, invoke, callbackOptions, remoteInstanceId);
|
|
730
1113
|
},
|
|
731
1114
|
getAndRemovePendingPromise(id) {
|
|
732
1115
|
const callbacks = pending.get(id);
|
|
@@ -755,6 +1138,21 @@ function createClientContext(getMessageChannel, options = {}) {
|
|
|
755
1138
|
const channel = getMessageChannel();
|
|
756
1139
|
const persistent = isListenerRegistrationPath(path);
|
|
757
1140
|
const serializedArgs = args.map((arg) => serialize(arg, registerFunction, { persistent }));
|
|
1141
|
+
if (isListenerRemovalPath(path)) {
|
|
1142
|
+
const releasedIds = [];
|
|
1143
|
+
for (const original of args) {
|
|
1144
|
+
if (typeof original !== 'function')
|
|
1145
|
+
continue;
|
|
1146
|
+
const fnId = functionIds.get(original);
|
|
1147
|
+
if (fnId) {
|
|
1148
|
+
releasedIds.push(fnId);
|
|
1149
|
+
functionCache.delete(fnId);
|
|
1150
|
+
persistentFunctionCache.delete(fnId);
|
|
1151
|
+
functionIds.delete(original);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
notifyReleaseCallbacks(releasedIds);
|
|
1155
|
+
}
|
|
758
1156
|
channel.getSender().sendMessage('invokeRequest', {
|
|
759
1157
|
id,
|
|
760
1158
|
path,
|
|
@@ -803,10 +1201,22 @@ function createClientContext(getMessageChannel, options = {}) {
|
|
|
803
1201
|
});
|
|
804
1202
|
});
|
|
805
1203
|
},
|
|
806
|
-
|
|
1204
|
+
handleRemoteDestroy(remoteInstanceId) {
|
|
1205
|
+
remoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1206
|
+
persistentRemoteCallbacksByOwner.delete(remoteInstanceId);
|
|
1207
|
+
},
|
|
1208
|
+
destroy(notifyRemote = true) {
|
|
807
1209
|
if (destroyed)
|
|
808
1210
|
return;
|
|
809
1211
|
destroyed = true;
|
|
1212
|
+
if (notifyRemote) {
|
|
1213
|
+
try {
|
|
1214
|
+
getMessageChannel().getSender().sendMessage('destroyEndpoint', { instanceId });
|
|
1215
|
+
}
|
|
1216
|
+
catch {
|
|
1217
|
+
// channel may already be torn down
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
810
1220
|
for (const [id, callbacks] of pending) {
|
|
811
1221
|
clearTimeout(callbacks.timer);
|
|
812
1222
|
callbacks.reject(new Error(`Channel has been destroyed before call ${id} completed`));
|
|
@@ -814,8 +1224,9 @@ function createClientContext(getMessageChannel, options = {}) {
|
|
|
814
1224
|
pending.clear();
|
|
815
1225
|
functionCache.clear();
|
|
816
1226
|
persistentFunctionCache.clear();
|
|
817
|
-
|
|
818
|
-
|
|
1227
|
+
functionIds.clear();
|
|
1228
|
+
remoteCallbacksByOwner.clear();
|
|
1229
|
+
persistentRemoteCallbacksByOwner.clear();
|
|
819
1230
|
},
|
|
820
1231
|
};
|
|
821
1232
|
}
|
|
@@ -823,6 +1234,43 @@ function createId(prefix) {
|
|
|
823
1234
|
return `${prefix}-${nanoid()}`;
|
|
824
1235
|
}
|
|
825
1236
|
|
|
1237
|
+
const EMPTY_TARGET = () => undefined;
|
|
1238
|
+
const INTERCEPTED_STRING_PROPS = new Set([
|
|
1239
|
+
'then',
|
|
1240
|
+
'catch',
|
|
1241
|
+
'finally',
|
|
1242
|
+
'toJSON',
|
|
1243
|
+
'toString',
|
|
1244
|
+
'valueOf',
|
|
1245
|
+
'nodeType',
|
|
1246
|
+
'tagName',
|
|
1247
|
+
'asymmetricMatch',
|
|
1248
|
+
'$$typeof',
|
|
1249
|
+
'@@__IMMUTABLE_ITERABLE__@@',
|
|
1250
|
+
'@@__IMMUTABLE_RECORD__@@',
|
|
1251
|
+
'length',
|
|
1252
|
+
'name',
|
|
1253
|
+
'prototype',
|
|
1254
|
+
'arguments',
|
|
1255
|
+
'caller',
|
|
1256
|
+
'bind',
|
|
1257
|
+
'call',
|
|
1258
|
+
'apply',
|
|
1259
|
+
]);
|
|
1260
|
+
const INTERCEPTED_NONE_SYMBOLS = new Set([
|
|
1261
|
+
Symbol.toStringTag,
|
|
1262
|
+
Symbol.iterator,
|
|
1263
|
+
Symbol.asyncIterator,
|
|
1264
|
+
Symbol.isConcatSpreadable,
|
|
1265
|
+
]);
|
|
1266
|
+
const PROXY_DESCRIPTION = '[chrome-in-iframe proxy]';
|
|
1267
|
+
function toPrimitiveDescriptor() {
|
|
1268
|
+
return (hint) => {
|
|
1269
|
+
if (hint === 'number')
|
|
1270
|
+
return NaN;
|
|
1271
|
+
return PROXY_DESCRIPTION;
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
826
1274
|
function resolvePath(node) {
|
|
827
1275
|
const path = [];
|
|
828
1276
|
let current = node;
|
|
@@ -834,11 +1282,14 @@ function resolvePath(node) {
|
|
|
834
1282
|
}
|
|
835
1283
|
function createClientProxy(invoke, accessProperty) {
|
|
836
1284
|
const normalizePropertyPath = (basePath, parts) => {
|
|
1285
|
+
if (parts.length === 1 && typeof parts[0] === 'string') {
|
|
1286
|
+
return [...basePath, ...parts[0].split('.').filter(Boolean)];
|
|
1287
|
+
}
|
|
837
1288
|
const extraPath = parts.flatMap((part) => {
|
|
838
1289
|
if (Array.isArray(part))
|
|
839
1290
|
return part.filter(isPathKey);
|
|
840
1291
|
if (typeof part === 'string')
|
|
841
|
-
return part
|
|
1292
|
+
return [part];
|
|
842
1293
|
if (typeof part === 'symbol')
|
|
843
1294
|
return [part];
|
|
844
1295
|
return [];
|
|
@@ -848,19 +1299,27 @@ function createClientProxy(invoke, accessProperty) {
|
|
|
848
1299
|
function createHandler(node) {
|
|
849
1300
|
return {
|
|
850
1301
|
get(_target, prop) {
|
|
851
|
-
if (prop === '
|
|
1302
|
+
if (typeof prop === 'string') {
|
|
1303
|
+
if (INTERCEPTED_STRING_PROPS.has(prop))
|
|
1304
|
+
return undefined;
|
|
1305
|
+
if (prop === '$get') {
|
|
1306
|
+
return (...parts) => {
|
|
1307
|
+
if (!accessProperty) {
|
|
1308
|
+
return Promise.reject(new Error('Property access is not configured'));
|
|
1309
|
+
}
|
|
1310
|
+
const basePath = node ? resolvePath(node) : [];
|
|
1311
|
+
return accessProperty(normalizePropertyPath(basePath, parts));
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
else if (prop === Symbol.toPrimitive) {
|
|
1316
|
+
return toPrimitiveDescriptor();
|
|
1317
|
+
}
|
|
1318
|
+
else if (INTERCEPTED_NONE_SYMBOLS.has(prop)) {
|
|
852
1319
|
return undefined;
|
|
853
|
-
if (prop === '$get') {
|
|
854
|
-
return (...parts) => {
|
|
855
|
-
if (!accessProperty) {
|
|
856
|
-
return Promise.reject(new Error('Property access is not configured'));
|
|
857
|
-
}
|
|
858
|
-
const basePath = node ? resolvePath(node) : [];
|
|
859
|
-
return accessProperty(normalizePropertyPath(basePath, parts));
|
|
860
|
-
};
|
|
861
1320
|
}
|
|
862
1321
|
const childNode = node ? { parent: node, key: prop } : { key: prop };
|
|
863
|
-
return new Proxy(
|
|
1322
|
+
return new Proxy(EMPTY_TARGET, createHandler(childNode));
|
|
864
1323
|
},
|
|
865
1324
|
apply(_target, _thisArg, argArray) {
|
|
866
1325
|
const path = node ? resolvePath(node) : [];
|
|
@@ -868,7 +1327,7 @@ function createClientProxy(invoke, accessProperty) {
|
|
|
868
1327
|
},
|
|
869
1328
|
};
|
|
870
1329
|
}
|
|
871
|
-
return new Proxy(
|
|
1330
|
+
return new Proxy(EMPTY_TARGET, createHandler());
|
|
872
1331
|
}
|
|
873
1332
|
function isPathKey(value) {
|
|
874
1333
|
return typeof value === 'string' || typeof value === 'symbol';
|
|
@@ -883,8 +1342,9 @@ function createEndpoint(options) {
|
|
|
883
1342
|
}
|
|
884
1343
|
return channelRef.current;
|
|
885
1344
|
};
|
|
886
|
-
const
|
|
887
|
-
const
|
|
1345
|
+
const instanceId = `ep-${nanoid()}`;
|
|
1346
|
+
const context = createClientContext(getChannel, instanceId, options.contextOptions);
|
|
1347
|
+
const channel = createMessageChannel(options.poster, options.key ?? 'default', instanceId, context, processorRegistry);
|
|
888
1348
|
channelRef.current = channel;
|
|
889
1349
|
processorRegistry.register('invokeRequest', handleInvokeRequest);
|
|
890
1350
|
processorRegistry.register('invokeResponse', handleInvokeResponse);
|
|
@@ -892,24 +1352,28 @@ function createEndpoint(options) {
|
|
|
892
1352
|
processorRegistry.register('accessPropertyResponse', handleAccessPropertyResponse);
|
|
893
1353
|
processorRegistry.register('invokeFunctionByIdRequest', handleCallbackInvoke);
|
|
894
1354
|
processorRegistry.register('invokeFunctionByIdResponse', handleCallbackInvokeResponse);
|
|
1355
|
+
processorRegistry.register('releaseCallbacks', handleReleaseCallbacks);
|
|
1356
|
+
processorRegistry.register('destroyEndpoint', handleDestroyEndpoint);
|
|
895
1357
|
const proxy = createClientProxy((path, args) => context.invoke(path, args), (path) => context.accessProperty(path));
|
|
896
1358
|
return {
|
|
897
1359
|
proxy,
|
|
1360
|
+
getContext: () => context,
|
|
898
1361
|
destroy() {
|
|
1362
|
+
context.destroy(true);
|
|
899
1363
|
channel.destroy();
|
|
900
|
-
context.destroy();
|
|
901
1364
|
},
|
|
902
1365
|
};
|
|
903
1366
|
}
|
|
904
1367
|
|
|
905
1368
|
function setupInMainWindow(options) {
|
|
1369
|
+
const resolveTargetOrigin = createTargetOriginResolver(() => options.iframe, options.targetOrigin);
|
|
906
1370
|
const poster = createWindowPoster({
|
|
907
1371
|
postMessage: (message) => {
|
|
908
1372
|
const contentWindow = options.iframe.contentWindow;
|
|
909
1373
|
if (!contentWindow) {
|
|
910
1374
|
throw new Error('chrome-in-iframe: iframe contentWindow is not available');
|
|
911
1375
|
}
|
|
912
|
-
contentWindow.postMessage(message,
|
|
1376
|
+
contentWindow.postMessage(message, resolveTargetOrigin());
|
|
913
1377
|
},
|
|
914
1378
|
getExpectedSource: () => options.iframe.contentWindow,
|
|
915
1379
|
allowedOrigin: options.allowedOrigin,
|
|
@@ -920,6 +1384,10 @@ function setupInMainWindow(options) {
|
|
|
920
1384
|
contextOptions: {
|
|
921
1385
|
delegateTarget: options.delegateTarget,
|
|
922
1386
|
timeout: options.timeout,
|
|
1387
|
+
functionCacheMax: options.functionCacheMax,
|
|
1388
|
+
functionCacheTtl: options.functionCacheTtl,
|
|
1389
|
+
remoteCallbackCacheMax: options.remoteCallbackCacheMax,
|
|
1390
|
+
remoteCallbackCacheTtl: options.remoteCallbackCacheTtl,
|
|
923
1391
|
},
|
|
924
1392
|
});
|
|
925
1393
|
const proxy = endpoint.proxy;
|
|
@@ -930,9 +1398,10 @@ function setupInMainWindow(options) {
|
|
|
930
1398
|
};
|
|
931
1399
|
}
|
|
932
1400
|
function setupInIframe(options = {}) {
|
|
1401
|
+
const resolveTargetOrigin = createParentTargetOriginResolver(options.targetOrigin);
|
|
933
1402
|
const poster = createWindowPoster({
|
|
934
1403
|
postMessage: (message) => {
|
|
935
|
-
window.parent.postMessage(message,
|
|
1404
|
+
window.parent.postMessage(message, resolveTargetOrigin());
|
|
936
1405
|
},
|
|
937
1406
|
getExpectedSource: () => window.parent,
|
|
938
1407
|
allowedOrigin: options.allowedOrigin,
|
|
@@ -942,6 +1411,10 @@ function setupInIframe(options = {}) {
|
|
|
942
1411
|
key: options.key,
|
|
943
1412
|
contextOptions: {
|
|
944
1413
|
timeout: options.timeout,
|
|
1414
|
+
functionCacheMax: options.functionCacheMax,
|
|
1415
|
+
functionCacheTtl: options.functionCacheTtl,
|
|
1416
|
+
remoteCallbackCacheMax: options.remoteCallbackCacheMax,
|
|
1417
|
+
remoteCallbackCacheTtl: options.remoteCallbackCacheTtl,
|
|
945
1418
|
},
|
|
946
1419
|
});
|
|
947
1420
|
const proxy = endpoint.proxy;
|
|
@@ -1007,5 +1480,67 @@ function getGlobalChrome() {
|
|
|
1007
1480
|
}
|
|
1008
1481
|
return chromeApi;
|
|
1009
1482
|
}
|
|
1483
|
+
function createTargetOriginResolver(getIframe, explicit) {
|
|
1484
|
+
if (explicit)
|
|
1485
|
+
return () => explicit;
|
|
1486
|
+
return () => deriveOriginFromIframe(getIframe());
|
|
1487
|
+
}
|
|
1488
|
+
function createParentTargetOriginResolver(explicit) {
|
|
1489
|
+
if (explicit)
|
|
1490
|
+
return () => explicit;
|
|
1491
|
+
return () => deriveParentOrigin();
|
|
1492
|
+
}
|
|
1493
|
+
function deriveOriginFromIframe(iframe) {
|
|
1494
|
+
const src = iframe.src || iframe.getAttribute('src') || '';
|
|
1495
|
+
const origin = parseOrigin(src);
|
|
1496
|
+
if (origin)
|
|
1497
|
+
return origin;
|
|
1498
|
+
try {
|
|
1499
|
+
if (iframe.contentWindow && iframe.contentWindow.location) {
|
|
1500
|
+
const loc = iframe.contentWindow.location;
|
|
1501
|
+
if (loc.origin && loc.origin !== 'null')
|
|
1502
|
+
return loc.origin;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
catch {
|
|
1506
|
+
// cross-origin contentWindow.location access throws — fall through
|
|
1507
|
+
}
|
|
1508
|
+
if (typeof console !== 'undefined') {
|
|
1509
|
+
console.warn("chrome-in-iframe: unable to derive iframe origin automatically; falling back to '*'. " +
|
|
1510
|
+
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
1511
|
+
}
|
|
1512
|
+
return '*';
|
|
1513
|
+
}
|
|
1514
|
+
function deriveParentOrigin() {
|
|
1515
|
+
if (typeof document !== 'undefined') {
|
|
1516
|
+
const referrer = document.referrer;
|
|
1517
|
+
const origin = parseOrigin(referrer);
|
|
1518
|
+
if (origin)
|
|
1519
|
+
return origin;
|
|
1520
|
+
}
|
|
1521
|
+
if (typeof location !== 'undefined' && location.ancestorOrigins && location.ancestorOrigins.length > 0) {
|
|
1522
|
+
const first = location.ancestorOrigins[0];
|
|
1523
|
+
if (first && first !== 'null')
|
|
1524
|
+
return first;
|
|
1525
|
+
}
|
|
1526
|
+
if (typeof console !== 'undefined') {
|
|
1527
|
+
console.warn("chrome-in-iframe: unable to derive parent origin automatically; falling back to '*'. " +
|
|
1528
|
+
'Pass `targetOrigin` explicitly to lock the destination.');
|
|
1529
|
+
}
|
|
1530
|
+
return '*';
|
|
1531
|
+
}
|
|
1532
|
+
function parseOrigin(url) {
|
|
1533
|
+
if (!url)
|
|
1534
|
+
return null;
|
|
1535
|
+
try {
|
|
1536
|
+
const parsed = new URL(url, typeof location !== 'undefined' ? location.href : 'http://localhost');
|
|
1537
|
+
if (parsed.origin && parsed.origin !== 'null')
|
|
1538
|
+
return parsed.origin;
|
|
1539
|
+
}
|
|
1540
|
+
catch {
|
|
1541
|
+
return null;
|
|
1542
|
+
}
|
|
1543
|
+
return null;
|
|
1544
|
+
}
|
|
1010
1545
|
|
|
1011
1546
|
export { connectChromeInIframe, exposeChromeInIframe, setupInIframe, setupInMainWindow };
|