This commit is contained in:
Simon Knott 2025-02-27 18:23:20 -03:00 committed by GitHub
commit 0ee4af8053
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 52 additions and 15 deletions

View file

@ -1530,10 +1530,6 @@ Returns storage state for this browser context, contains current cookies, local
Set to `true` to include IndexedDB in the storage state snapshot. Set to `true` to include IndexedDB in the storage state snapshot.
If your application uses IndexedDB to store authentication tokens, like Firebase Authentication, enable this. If your application uses IndexedDB to store authentication tokens, like Firebase Authentication, enable this.
:::note
IndexedDBs with typed arrays are currently not supported.
:::
## property: BrowserContext.tracing ## property: BrowserContext.tracing
* since: v1.12 * since: v1.12
- type: <[Tracing]> - type: <[Tracing]>

View file

@ -9273,9 +9273,6 @@ export interface BrowserContext {
/** /**
* Set to `true` to include IndexedDB in the storage state snapshot. If your application uses IndexedDB to store * Set to `true` to include IndexedDB in the storage state snapshot. If your application uses IndexedDB to store
* authentication tokens, like Firebase Authentication, enable this. * authentication tokens, like Firebase Authentication, enable this.
*
* **NOTE** IndexedDBs with typed arrays are currently not supported.
*
*/ */
indexedDB?: boolean; indexedDB?: boolean;

View file

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
type TypedArrayKind = 'i8' | 'ui8' | 'ui8c' | 'i16' | 'ui16' | 'i32' | 'ui32' | 'f32' | 'f64' | 'bi64' | 'bui64';
export type SerializedValue = export type SerializedValue =
undefined | boolean | number | string | undefined | boolean | number | string |
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } | { v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
@ -25,7 +27,8 @@ export type SerializedValue =
{ a: SerializedValue[], id: number } | { a: SerializedValue[], id: number } |
{ o: { k: string, v: SerializedValue }[], id: number } | { o: { k: string, v: SerializedValue }[], id: number } |
{ ref: number } | { ref: number } |
{ h: number }; { h: number } |
{ ta: { b: string, k: TypedArrayKind } };
export type HandleOrValue = { h: number } | { fallThrough: any }; export type HandleOrValue = { h: number } | { fallThrough: any };
@ -68,6 +71,42 @@ export function source() {
} }
} }
const typedArrayConstructors: Record<TypedArrayKind, Function> = {
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
// TODO: add Float16Array once it's in baseline
f32: Float32Array,
f64: Float64Array,
bi64: BigInt64Array,
bui64: BigUint64Array,
};
function typedArrayToBase64(array: any) {
if (globalThis.Buffer)
return Buffer.from(array).toString('base64');
const binary = Array.from(new Uint8Array(array.buffer)).map(b => String.fromCharCode(b)).join('');
return btoa(binary);
}
function base64ToTypedArray(base64: string, TypedArrayConstructor: any) {
if (globalThis.Buffer) {
const buf = Buffer.from(base64, 'base64');
return new TypedArrayConstructor(buf.buffer, buf.byteOffset, buf.byteLength / buf.BYTES_PER_ELEMENT);
}
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++)
bytes[i] = binary.charCodeAt(i);
return new TypedArrayConstructor(bytes.buffer);
}
function parseEvaluationResultValue(value: SerializedValue, handles: any[] = [], refs: Map<number, object> = new Map()): any { function parseEvaluationResultValue(value: SerializedValue, handles: any[] = [], refs: Map<number, object> = new Map()): any {
if (Object.is(value, undefined)) if (Object.is(value, undefined))
return undefined; return undefined;
@ -119,6 +158,8 @@ export function source() {
} }
if ('h' in value) if ('h' in value)
return handles[value.h]; return handles[value.h];
if ('ta' in value)
return base64ToTypedArray(value.ta.b, typedArrayConstructors[value.ta.k]);
} }
return value; return value;
} }
@ -189,6 +230,10 @@ export function source() {
return { u: value.toJSON() }; return { u: value.toJSON() };
if (isRegExp(value)) if (isRegExp(value))
return { r: { p: value.source, f: value.flags } }; return { r: { p: value.source, f: value.flags } };
for (const [k, ctor] of Object.entries(typedArrayConstructors) as [TypedArrayKind, Function][]) {
if (value instanceof ctor)
return { ta: { b: typedArrayToBase64(value), k } };
}
const id = visitorInfo.visited.get(value); const id = visitorInfo.visited.get(value);
if (id) if (id)

View file

@ -9273,9 +9273,6 @@ export interface BrowserContext {
/** /**
* Set to `true` to include IndexedDB in the storage state snapshot. If your application uses IndexedDB to store * Set to `true` to include IndexedDB in the storage state snapshot. If your application uses IndexedDB to store
* authentication tokens, like Firebase Authentication, enable this. * authentication tokens, like Firebase Authentication, enable this.
*
* **NOTE** IndexedDBs with typed arrays are currently not supported.
*
*/ */
indexedDB?: boolean; indexedDB?: boolean;

View file

@ -100,7 +100,7 @@ it('should round-trip through the file', async ({ contextFactory }, testInfo) =>
.put({ name: 'foo', date: new Date(0) }); .put({ name: 'foo', date: new Date(0) });
transaction transaction
.objectStore('store2') .objectStore('store2')
.put('bar', 'foo'); .put(new TextEncoder().encode('bar'), 'foo');
transaction.addEventListener('complete', resolve); transaction.addEventListener('complete', resolve);
transaction.addEventListener('error', reject); transaction.addEventListener('error', reject);
}; };
@ -126,16 +126,18 @@ it('should round-trip through the file', async ({ contextFactory }, testInfo) =>
expect(cookie).toEqual('username=John Doe'); expect(cookie).toEqual('username=John Doe');
const idbValues = await page2.evaluate(() => new Promise((resolve, reject) => { const idbValues = await page2.evaluate(() => new Promise((resolve, reject) => {
const openRequest = indexedDB.open('db', 42); const openRequest = indexedDB.open('db', 42);
openRequest.addEventListener('success', () => { openRequest.addEventListener('success', async () => {
const db = openRequest.result; const db = openRequest.result;
const transaction = db.transaction(['store', 'store2'], 'readonly'); const transaction = db.transaction(['store', 'store2'], 'readonly');
const request1 = transaction.objectStore('store').get('foo'); const request1 = transaction.objectStore('store').get('foo');
const request2 = transaction.objectStore('store2').get('foo'); const request2 = transaction.objectStore('store2').get('foo');
Promise.all([request1, request2].map(request => new Promise((resolve, reject) => { const [result1, result2] = await Promise.all([request1, request2].map(request => new Promise((resolve, reject) => {
request.addEventListener('success', () => resolve(request.result)); request.addEventListener('success', () => resolve(request.result));
request.addEventListener('error', () => reject(request.error)); request.addEventListener('error', () => reject(request.error));
}))).then(resolve, reject); })));
resolve([result1, new TextDecoder().decode(result2 as any)]);
}); });
openRequest.addEventListener('error', () => reject(openRequest.error)); openRequest.addEventListener('error', () => reject(openRequest.error));
})); }));