buninu-browser 0.2.0 → 0.2.2
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/AGENTS.md +4 -0
- package/README.md +74 -9
- package/bunfig.toml +1 -0
- package/buninu-browser.js +4 -0
- package/index.js +5 -0
- package/lib/audio/piano.js +24 -1
- package/lib/audio/pulse-client.js +120 -12
- package/lib/cdp-server.js +19 -12
- package/lib/happy-dom/parser.js +24 -4
- package/lib/happy-dom/selector-matcher.js +83 -0
- package/lib/headless-shell.js +120 -28
- package/lib/input/hit-test.js +25 -7
- package/lib/layout/text-layout.js +365 -54
- package/lib/log.js +5 -2
- package/lib/media/byte-pipe.js +82 -0
- package/lib/media/position-audio.js +42 -0
- package/lib/network/network-service.js +326 -46
- package/lib/paint/display-list.js +322 -21
- package/lib/paint/gradient.js +11 -5
- package/lib/render-tree/index.js +62 -2
- package/lib/render-tree/svg.js +2 -1
- package/lib/renderer/bindings.js +989 -72
- package/lib/renderer/canvas-2d.js +92 -0
- package/lib/renderer/host.js +159 -9
- package/lib/renderer/interfaces.js +215 -0
- package/lib/renderer/main.js +160 -12
- package/lib/renderer/media-source.js +819 -0
- package/lib/renderer/messaging.js +411 -0
- package/lib/renderer/native-functions.js +113 -0
- package/lib/renderer/page-realm.js +1407 -200
- package/lib/renderer/page-renderer.js +1899 -394
- package/lib/renderer/rejections.js +31 -0
- package/lib/renderer/rewrite-worker.js +22 -0
- package/lib/renderer/script-rewrite.js +805 -57
- package/lib/renderer/trusted-types.js +129 -0
- package/lib/renderer/workers.js +327 -0
- package/lib/screenshot-cli.js +2 -0
- package/lib/style/computed-style.js +463 -242
- package/lib/style/selector-keys.js +302 -0
- package/lib/style/selector-traits.js +240 -0
- package/lib/user-agent.js +14 -2
- package/package.json +4 -1
- package/patches/happy-dom/0002-use-bun-platform-adapters.patch +139 -19
- package/patches/happy-dom/0006-parse-css-escapes-in-selectors.patch +115 -0
- package/patches/happy-dom/README.md +26 -0
- package/patches/ses/0002-embedder-native-functions-source-text-sloppy-code.patch +106 -0
- package/patches/ses/0003-cache-sloppy-evaluators.patch +47 -0
- package/patches/ses/README.md +4 -0
- package/test/canvas-2d.test.js +55 -0
- package/test/compositor.test.js +3 -1
- package/test/computed-style.test.js +98 -1
- package/test/display-list.test.js +74 -1
- package/test/download.test.js +35 -0
- package/test/fixtures/long-video.mp4 +0 -0
- package/test/fixtures/media-sync.js +57 -0
- package/test/happy-dom-platform-adapters.test.js +25 -0
- package/test/headless-shell.test.js +29 -0
- package/test/hit-test.test.js +25 -1
- package/test/log.test.js +4 -1
- package/test/media-source.test.js +156 -0
- package/test/media-sync.test.js +12 -0
- package/test/mobile.test.js +2 -2
- package/test/navigation.test.js +61 -0
- package/test/network-service.test.js +81 -0
- package/test/page-scripts.test.js +1094 -7
- package/test/position-audio.test.js +67 -0
- package/test/positioning.test.js +95 -1
- package/test/pulse-client.test.js +69 -0
- package/test/render-tree.test.js +22 -1
- package/test/renderer-host.test.js +5 -2
- package/test/screenshot-cli.test.js +25 -1
- package/test/script-rewrite.test.js +111 -6
- package/test/selector-keys.test.js +39 -0
- package/test/selector-traits.test.js +19 -0
- package/test/setup.js +5 -0
- package/test/text-layout.test.js +69 -1
- package/test/user-agent.test.js +60 -4
- package/vendor/happy-dom/packages/happy-dom/src/nodes/html-image-element/HTMLImageElement.ts +52 -22
- package/vendor/happy-dom/packages/happy-dom/src/query-selector/SelectorParser.ts +18 -17
- package/vendor/ses/dist/ses.mjs +52 -6
package/test/user-agent.test.js
CHANGED
|
@@ -2,7 +2,16 @@ import { expect, test } from "bun:test";
|
|
|
2
2
|
import { CdpServer } from "../lib/cdp-server.js";
|
|
3
3
|
import { createBrowser } from "../lib/headless-shell.js";
|
|
4
4
|
import { MOBILE_USER_AGENT } from "../lib/renderer/flags.js";
|
|
5
|
-
import { CHROMIUM_VERSION, DESKTOP_USER_AGENT, desktopUserAgent } from "../lib/user-agent.js";
|
|
5
|
+
import { BROWSER_PRODUCT, CHROMIUM_VERSION, DESKTOP_USER_AGENT, WEBKIT_USER_AGENT, desktopUserAgent, webkitFromArgs } from "../lib/user-agent.js";
|
|
6
|
+
|
|
7
|
+
test("CLI identity defaults to WebKit, last desktop flag wins and mobile takes priority", () => {
|
|
8
|
+
for (const [args, expected] of [
|
|
9
|
+
[[], true], [["--webkit"], true], [["--chromium"], false],
|
|
10
|
+
[["--webkit", "--chromium"], false], [["--chromium", "--webkit"], true],
|
|
11
|
+
[["--mobile"], false], [["--mobile", "--webkit"], false],
|
|
12
|
+
[["--webkit", "--mobile"], false], [["--mobile", "--chromium"], false],
|
|
13
|
+
]) expect(webkitFromArgs(args)).toBe(expected);
|
|
14
|
+
});
|
|
6
15
|
|
|
7
16
|
test("desktop user agent uses the same version on each platform", () => {
|
|
8
17
|
for (const [platform, arch, system] of [
|
|
@@ -19,17 +28,17 @@ test("mobile uses current Chrome for Android rather than WebView", () => {
|
|
|
19
28
|
expect(MOBILE_USER_AGENT).toBe(`Mozilla/5.0 (Linux; Android 16; ASUSAI2501C Build/BQ2A.250525.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROMIUM_VERSION} Mobile Safari/537.36`);
|
|
20
29
|
});
|
|
21
30
|
|
|
22
|
-
test("
|
|
31
|
+
test("Chromium opt-out reports the same identity through CDP, HTTP and navigator", async () => {
|
|
23
32
|
let requestUserAgent;
|
|
24
33
|
const site = Bun.serve({ port: 0, fetch(request) {
|
|
25
34
|
requestUserAgent = request.headers.get("user-agent");
|
|
26
35
|
return new Response("<!doctype html><title>UA test</title>", { headers: { "content-type": "text/html" } });
|
|
27
36
|
} });
|
|
28
|
-
const browser = await createBrowser({ spareRenderer: false });
|
|
37
|
+
const browser = await createBrowser({ spareRenderer: false, webkit: false });
|
|
29
38
|
const cdp = CdpServer.create(browser.context).listen(0, "127.0.0.1");
|
|
30
39
|
try {
|
|
31
40
|
const version = await fetch(`http://127.0.0.1:${cdp.port}/json/version`).then((response) => response.json());
|
|
32
|
-
expect(version.Browser).toBe(
|
|
41
|
+
expect(version.Browser).toBe(BROWSER_PRODUCT);
|
|
33
42
|
expect(version["User-Agent"]).toBe(DESKTOP_USER_AGENT);
|
|
34
43
|
await browser.context.navigate(`http://127.0.0.1:${site.port}/`);
|
|
35
44
|
expect(requestUserAgent).toBe(DESKTOP_USER_AGENT);
|
|
@@ -40,3 +49,50 @@ test("CDP discovery, HTTP requests, and navigator report the same desktop user a
|
|
|
40
49
|
site.stop(true);
|
|
41
50
|
}
|
|
42
51
|
});
|
|
52
|
+
|
|
53
|
+
test.each([{}, { webkit: true }])("default and explicit WebKit identity is consistent across CDP, requests and navigator: %j", async (options) => {
|
|
54
|
+
let headers;
|
|
55
|
+
const site = Bun.serve({ port: 0, fetch(request) {
|
|
56
|
+
headers = request.headers;
|
|
57
|
+
return new Response("<!doctype html><title>identity</title>", { headers: { "content-type": "text/html" } });
|
|
58
|
+
} });
|
|
59
|
+
const browser = await createBrowser({ spareRenderer: false, ...options });
|
|
60
|
+
const cdp = CdpServer.create(browser.context).listen(0, "127.0.0.1");
|
|
61
|
+
try {
|
|
62
|
+
// Default and explicit WebKit profiles both resist a CDP client's Chrome UA.
|
|
63
|
+
await browser.context.setUserAgent(DESKTOP_USER_AGENT);
|
|
64
|
+
const version = await fetch(`http://127.0.0.1:${cdp.port}/json/version`).then(r => r.json());
|
|
65
|
+
expect(version.Browser).toBe(BROWSER_PRODUCT);
|
|
66
|
+
expect(version["User-Agent"]).toBe(WEBKIT_USER_AGENT);
|
|
67
|
+
await browser.context.navigate(`http://127.0.0.1:${site.port}/`);
|
|
68
|
+
expect(headers.get("user-agent")).toBe(WEBKIT_USER_AGENT);
|
|
69
|
+
expect(headers.has("sec-ch-ua")).toBe(false);
|
|
70
|
+
expect(await browser.context.evaluate(`[
|
|
71
|
+
navigator.userAgent, navigator.vendor, navigator.platform,
|
|
72
|
+
'userAgentData' in navigator, typeof NavigatorUAData, typeof window.chrome,
|
|
73
|
+
]`)).toEqual([WEBKIT_USER_AGENT, "Apple Computer, Inc.", "MacIntel", false, "undefined", "undefined"]);
|
|
74
|
+
} finally {
|
|
75
|
+
cdp.stop(true);
|
|
76
|
+
browser.close();
|
|
77
|
+
site.stop(true);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("scripts added for new documents run before the page's own scripts, in every later document", async () => {
|
|
82
|
+
const site = Bun.serve({ port: 0, fetch: () => new Response(
|
|
83
|
+
"<!doctype html><script>window.seenByPage = window.early;</script>",
|
|
84
|
+
{ headers: { "content-type": "text/html" } },
|
|
85
|
+
) });
|
|
86
|
+
const browser = await createBrowser({ spareRenderer: false });
|
|
87
|
+
try {
|
|
88
|
+
const first = browser.context.addInitScript("window.early = (window.early ?? 0) + 1;");
|
|
89
|
+
await browser.context.navigate(`http://127.0.0.1:${site.port}/`);
|
|
90
|
+
expect(await browser.context.evaluate("window.seenByPage")).toBe(1);
|
|
91
|
+
browser.context.removeInitScript(first);
|
|
92
|
+
await browser.context.navigate(`http://127.0.0.1:${site.port}/again`);
|
|
93
|
+
expect(await browser.context.evaluate("window.seenByPage")).toBe(undefined);
|
|
94
|
+
} finally {
|
|
95
|
+
browser.close();
|
|
96
|
+
site.stop(true);
|
|
97
|
+
}
|
|
98
|
+
});
|
package/vendor/happy-dom/packages/happy-dom/src/nodes/html-image-element/HTMLImageElement.ts
CHANGED
|
@@ -19,6 +19,8 @@ const DATA_URL_REGEX = /^\s*data:([^,]+)?,(.*)/;
|
|
|
19
19
|
export default class HTMLImageElement extends HTMLElement implements ICanvasShape {
|
|
20
20
|
public [PropertySymbol.tagName] = 'IMG';
|
|
21
21
|
public [PropertySymbol.complete] = true;
|
|
22
|
+
// Bumped whenever src changes: an image decoded for an earlier src is dropped.
|
|
23
|
+
#loadGeneration = 0;
|
|
22
24
|
public [PropertySymbol.naturalHeight] = 0;
|
|
23
25
|
public [PropertySymbol.naturalWidth] = 0;
|
|
24
26
|
public [PropertySymbol.loading] = 'auto';
|
|
@@ -357,6 +359,7 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
357
359
|
super[PropertySymbol.onSetAttribute](attribute, replacedAttribute);
|
|
358
360
|
|
|
359
361
|
if (attribute[PropertySymbol.name] === 'src') {
|
|
362
|
+
const generation = ++this.#loadGeneration;
|
|
360
363
|
const url = this.#parseUrl(attribute[PropertySymbol.value]);
|
|
361
364
|
|
|
362
365
|
if (!url) {
|
|
@@ -367,13 +370,13 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
367
370
|
return;
|
|
368
371
|
}
|
|
369
372
|
|
|
370
|
-
if (this.#loadDataUrl(url)) {
|
|
373
|
+
if (this.#loadDataUrl(url, generation)) {
|
|
371
374
|
return;
|
|
372
375
|
}
|
|
373
376
|
|
|
374
377
|
const settings = new WindowBrowserContext(this[PropertySymbol.window]).getSettings();
|
|
375
378
|
if (settings?.enableImageFileLoading) {
|
|
376
|
-
this.#loadUrlSource(this.src);
|
|
379
|
+
this.#loadUrlSource(this.src, generation);
|
|
377
380
|
}
|
|
378
381
|
}
|
|
379
382
|
}
|
|
@@ -385,6 +388,7 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
385
388
|
super[PropertySymbol.onRemoveAttribute](removedAttribute);
|
|
386
389
|
|
|
387
390
|
if (removedAttribute[PropertySymbol.name] === 'src') {
|
|
391
|
+
this.#loadGeneration++;
|
|
388
392
|
this[PropertySymbol.complete] = true;
|
|
389
393
|
this[PropertySymbol.naturalHeight] = 0;
|
|
390
394
|
this[PropertySymbol.naturalWidth] = 0;
|
|
@@ -415,27 +419,37 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
415
419
|
* Loads the image from the given data URL.
|
|
416
420
|
*
|
|
417
421
|
* @param src Source.
|
|
422
|
+
* @param generation The load this is (see #loadGeneration).
|
|
418
423
|
* @returns True if the image was loaded, false otherwise.
|
|
419
424
|
*/
|
|
420
|
-
#loadDataUrl(src: string): boolean {
|
|
425
|
+
#loadDataUrl(src: string, generation: number): boolean {
|
|
421
426
|
const dataUrlMatch = src.match(DATA_URL_REGEX);
|
|
422
427
|
|
|
423
428
|
if (dataUrlMatch) {
|
|
424
429
|
const buffer = Buffer.from(dataUrlMatch[2], 'base64');
|
|
425
|
-
this[PropertySymbol.complete] = true;
|
|
426
430
|
this[PropertySymbol.buffer] = buffer;
|
|
427
431
|
this[PropertySymbol.complete] = false;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
432
|
+
// The size is read asynchronously; a src set meanwhile wins.
|
|
433
|
+
ImageSize(buffer).then(
|
|
434
|
+
(dimensions) => {
|
|
435
|
+
if (generation !== this.#loadGeneration) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this[PropertySymbol.naturalHeight] = dimensions.height;
|
|
439
|
+
this[PropertySymbol.naturalWidth] = dimensions.width;
|
|
440
|
+
this[PropertySymbol.complete] = true;
|
|
441
|
+
this.dispatchEvent(new this[PropertySymbol.window].Event('load'));
|
|
442
|
+
},
|
|
443
|
+
() => {
|
|
444
|
+
if (generation !== this.#loadGeneration) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this[PropertySymbol.naturalHeight] = 0;
|
|
448
|
+
this[PropertySymbol.naturalWidth] = 0;
|
|
449
|
+
this[PropertySymbol.complete] = true;
|
|
450
|
+
this.dispatchEvent(new this[PropertySymbol.window].Event('error'));
|
|
451
|
+
}
|
|
452
|
+
);
|
|
439
453
|
return true;
|
|
440
454
|
}
|
|
441
455
|
return false;
|
|
@@ -445,8 +459,9 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
445
459
|
* Loads the image from the given source.
|
|
446
460
|
*
|
|
447
461
|
* @param src Source.
|
|
462
|
+
* @param generation The load this is (see #loadGeneration).
|
|
448
463
|
*/
|
|
449
|
-
async #loadUrlSource(src: string): Promise<void> {
|
|
464
|
+
async #loadUrlSource(src: string, generation: number): Promise<void> {
|
|
450
465
|
this[PropertySymbol.complete] = false;
|
|
451
466
|
this[PropertySymbol.naturalHeight] = 0;
|
|
452
467
|
this[PropertySymbol.naturalWidth] = 0;
|
|
@@ -460,6 +475,10 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
460
475
|
error = <Error>e;
|
|
461
476
|
}
|
|
462
477
|
|
|
478
|
+
if (generation !== this.#loadGeneration) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
463
482
|
if (error || !response?.ok) {
|
|
464
483
|
this[PropertySymbol.complete] = true;
|
|
465
484
|
this.dispatchEvent(new this[PropertySymbol.window].Event('error'));
|
|
@@ -478,23 +497,34 @@ export default class HTMLImageElement extends HTMLElement implements ICanvasShap
|
|
|
478
497
|
try {
|
|
479
498
|
buffer = await response.buffer();
|
|
480
499
|
} catch (e) {
|
|
500
|
+
if (generation !== this.#loadGeneration) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
481
503
|
this[PropertySymbol.complete] = true;
|
|
482
504
|
this.dispatchEvent(new this[PropertySymbol.window].Event('error'));
|
|
483
505
|
return;
|
|
484
506
|
}
|
|
485
507
|
|
|
486
|
-
this
|
|
508
|
+
if (generation !== this.#loadGeneration) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
487
512
|
this[PropertySymbol.buffer] = buffer;
|
|
488
513
|
|
|
514
|
+
let dimensions = { width: 0, height: 0 };
|
|
489
515
|
try {
|
|
490
|
-
|
|
491
|
-
this[PropertySymbol.naturalHeight] = dimensions.height;
|
|
492
|
-
this[PropertySymbol.naturalWidth] = dimensions.width;
|
|
516
|
+
dimensions = await ImageSize(buffer);
|
|
493
517
|
} catch (e) {
|
|
494
|
-
|
|
495
|
-
this[PropertySymbol.naturalWidth] = 0;
|
|
518
|
+
// Undecodable: no natural size.
|
|
496
519
|
}
|
|
497
520
|
|
|
521
|
+
if (generation !== this.#loadGeneration) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
this[PropertySymbol.naturalHeight] = dimensions.height;
|
|
526
|
+
this[PropertySymbol.naturalWidth] = dimensions.width;
|
|
527
|
+
this[PropertySymbol.complete] = true;
|
|
498
528
|
this.dispatchEvent(new this[PropertySymbol.window].Event('load'));
|
|
499
529
|
}
|
|
500
530
|
}
|
|
@@ -12,10 +12,11 @@ import type { TGlobalMatchFunction } from './TGlobalMatchFunction.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* Selector group RegExp.
|
|
14
14
|
*
|
|
15
|
-
* Group 1:
|
|
16
|
-
* Group 2:
|
|
15
|
+
* Group 1: An escaped character (e.g. "\:", "\31 "), skipped.
|
|
16
|
+
* Group 2: Combinator (" ", ",", "+", ">", "̣~")
|
|
17
|
+
* Group 3: Parentheses, brackets or quotes.
|
|
17
18
|
*/
|
|
18
|
-
const SELECTOR_GROUP_REGEXP = /(\s*[\s,+>~]\s*)|([\[\]\(\)"'])/g;
|
|
19
|
+
const SELECTOR_GROUP_REGEXP = /(\\[0-9a-fA-F]{1,6}\s?|\\[\s\S])|(\s*[\s,+>~]\s*)|([\[\]\(\)"'])/g;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Selector RegExp.
|
|
@@ -45,7 +46,7 @@ const SELECTOR_GROUP_REGEXP = /(\s*[\s,+>~]\s*)|([\[\]\(\)"'])/g;
|
|
|
45
46
|
* Group 23: Pseudo element (e.g. "::after", "::-webkit-inner-spin-button").
|
|
46
47
|
*/
|
|
47
48
|
const SELECTOR_REGEXP =
|
|
48
|
-
/(\*)|([a-zA-Z0-9\u00A0-\uFFFF-]+)|#(([a-zA-Z0-9\u00A0-\uFFFF_-]
|
|
49
|
+
/(\*)|((?:[a-zA-Z0-9\u00A0-\uFFFF-]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)|#(([a-zA-Z0-9\u00A0-\uFFFF_-]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)|\.(([a-zA-Z0-9\u00A0-\uFFFF_-]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)|\[(([a-zA-Z0-9-_]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)\]|\[(([a-zA-Z0-9-_]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)\s*([~|^$*]{0,1})\s*=\s*("((?:[^"\\]|\\[\s\S])*)"|'((?:[^'\\]|\\[\s\S])*)')\s*(s|i){0,1}\]|\[(([a-zA-Z0-9-_]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)\s*([~|^$*]{0,1})\s*=\s*(([a-zA-Z0-9\u00A0-\uFFFF_¤£-]|\\[0-9a-fA-F]{1,6}\s?|\\.)+)\]|:([a-zA-Z-]+)\s*\(.+\)|:([a-zA-Z-]+)|::([a-zA-Z-]+)/g;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* Selector pseudo RegExp.
|
|
@@ -55,11 +56,6 @@ const SELECTOR_REGEXP =
|
|
|
55
56
|
*/
|
|
56
57
|
const SELECTOR_PSEUDO_REGEXP = /:([a-zA-Z-]+)|([()])/g;
|
|
57
58
|
|
|
58
|
-
/**
|
|
59
|
-
* Escaped Character RegExp.
|
|
60
|
-
*/
|
|
61
|
-
const ESCAPED_CHARACTER_REGEXP = /\\/g;
|
|
62
|
-
|
|
63
59
|
/**
|
|
64
60
|
* CSS Escape RegExp.
|
|
65
61
|
*
|
|
@@ -187,6 +183,11 @@ export default class SelectorParser {
|
|
|
187
183
|
|
|
188
184
|
while ((match = regExp.exec(selector))) {
|
|
189
185
|
if (match[1]) {
|
|
186
|
+
// An escaped character (CSS Syntax §4.3.7) is part of the selector it is in,
|
|
187
|
+
// never a combinator, bracket or quote.
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (match[2]) {
|
|
190
191
|
// Matches combinator (" ", ",", "+", ">", "̣~")
|
|
191
192
|
// We should ignore combinators that are inside parentheses, brackets or apostrophes
|
|
192
193
|
|
|
@@ -198,7 +199,7 @@ export default class SelectorParser {
|
|
|
198
199
|
) {
|
|
199
200
|
const childSelector = selector.substring(lastIndex, match.index).trim();
|
|
200
201
|
|
|
201
|
-
switch (match[
|
|
202
|
+
switch (match[2].trim()) {
|
|
202
203
|
case ',':
|
|
203
204
|
selectorItem = this.getSelectorGroupItem(childSelector, combinator);
|
|
204
205
|
if (!selectorItem) {
|
|
@@ -262,7 +263,7 @@ export default class SelectorParser {
|
|
|
262
263
|
} else {
|
|
263
264
|
// Matches parentheses or brackets.
|
|
264
265
|
|
|
265
|
-
switch (match[
|
|
266
|
+
switch (match[3]) {
|
|
266
267
|
case '(':
|
|
267
268
|
if (depth.singleApostrophe === 0 && depth.doubleApostrophe === 0) {
|
|
268
269
|
depth.parentheses++;
|
|
@@ -365,22 +366,22 @@ export default class SelectorParser {
|
|
|
365
366
|
} else if (match[2]) {
|
|
366
367
|
// Matches tag name, e.g. "div"
|
|
367
368
|
|
|
368
|
-
selectorItem.tagName = match[2].toUpperCase();
|
|
369
|
+
selectorItem.tagName = SelectorParser.cssUnescape(match[2]).toUpperCase();
|
|
369
370
|
} else if (match[3]) {
|
|
370
371
|
// Matches ID, e.g. "#id"
|
|
371
372
|
|
|
372
|
-
selectorItem.id = match[3]
|
|
373
|
+
selectorItem.id = SelectorParser.cssUnescape(match[3]);
|
|
373
374
|
} else if (match[5]) {
|
|
374
375
|
// Matches class names, e.g. ".class1"
|
|
375
376
|
|
|
376
377
|
selectorItem.classNames = selectorItem.classNames || [];
|
|
377
|
-
selectorItem.classNames.push(match[5]
|
|
378
|
+
selectorItem.classNames.push(SelectorParser.cssUnescape(match[5]));
|
|
378
379
|
} else if (match[7]) {
|
|
379
380
|
// Matches attributes without value, e.g. [attr]
|
|
380
381
|
|
|
381
382
|
selectorItem.attributes = selectorItem.attributes || [];
|
|
382
383
|
selectorItem.attributes.push({
|
|
383
|
-
name: match[7]
|
|
384
|
+
name: SelectorParser.cssUnescape(match[7]),
|
|
384
385
|
operator: null,
|
|
385
386
|
value: null,
|
|
386
387
|
modifier: null,
|
|
@@ -393,7 +394,7 @@ export default class SelectorParser {
|
|
|
393
394
|
const unescapedValue = SelectorParser.cssUnescape(value);
|
|
394
395
|
selectorItem.attributes = selectorItem.attributes || [];
|
|
395
396
|
selectorItem.attributes.push({
|
|
396
|
-
name: match[9]
|
|
397
|
+
name: SelectorParser.cssUnescape(match[9]),
|
|
397
398
|
operator: match[11] || null,
|
|
398
399
|
value: unescapedValue,
|
|
399
400
|
modifier: <'s'>match[15] || null,
|
|
@@ -409,7 +410,7 @@ export default class SelectorParser {
|
|
|
409
410
|
const unescapedValue = SelectorParser.cssUnescape(match[19]);
|
|
410
411
|
selectorItem.attributes = selectorItem.attributes || [];
|
|
411
412
|
selectorItem.attributes.push({
|
|
412
|
-
name: match[16]
|
|
413
|
+
name: SelectorParser.cssUnescape(match[16]),
|
|
413
414
|
operator: match[18] || null,
|
|
414
415
|
value: unescapedValue,
|
|
415
416
|
modifier: null,
|
package/vendor/ses/dist/ses.mjs
CHANGED
|
@@ -9568,7 +9568,7 @@ function buildOptimizer(constants, name) {
|
|
|
9568
9568
|
${globalObjectOptimizer}
|
|
9569
9569
|
${moduleLexicalOptimizer}
|
|
9570
9570
|
return function() {
|
|
9571
|
-
'use strict';
|
|
9571
|
+
${context.sloppyCode ? '' : "'use strict';"}
|
|
9572
9572
|
return eval(arguments[0]);
|
|
9573
9573
|
};
|
|
9574
9574
|
}
|
|
@@ -9611,6 +9611,7 @@ const { Fail } = assert;
|
|
|
9611
9611
|
moduleLexicals = {},
|
|
9612
9612
|
globalTransforms = [],
|
|
9613
9613
|
sloppyGlobalsMode = false,
|
|
9614
|
+
sloppyCode = false,
|
|
9614
9615
|
}) => {
|
|
9615
9616
|
const scopeTerminator = sloppyGlobalsMode
|
|
9616
9617
|
? createSloppyGlobalsScopeTerminator(globalObject)
|
|
@@ -9623,6 +9624,7 @@ const { Fail } = assert;
|
|
|
9623
9624
|
moduleLexicals,
|
|
9624
9625
|
globalObject,
|
|
9625
9626
|
scopeTerminator,
|
|
9627
|
+
sloppyCode,
|
|
9626
9628
|
});
|
|
9627
9629
|
|
|
9628
9630
|
// Defer creating the actual evaluator to first use.
|
|
@@ -9715,6 +9717,14 @@ const nativeSuffix = ') { [native code] }';
|
|
|
9715
9717
|
// use this top level state so that multiple calls to `tameFunctionToString` are
|
|
9716
9718
|
// idempotent, rather than creating redundant indirections.
|
|
9717
9719
|
let markVirtualizedNativeFunction;
|
|
9720
|
+
let sourceTextTransform;
|
|
9721
|
+
// Taken (and deleted) by the embedder, like the native-function marker.
|
|
9722
|
+
Object.defineProperty(globalThis, Symbol.for('ses.setFunctionSourceTextTransform'), {
|
|
9723
|
+
value: transform => {
|
|
9724
|
+
sourceTextTransform = transform;
|
|
9725
|
+
},
|
|
9726
|
+
configurable: true,
|
|
9727
|
+
});
|
|
9718
9728
|
|
|
9719
9729
|
/**
|
|
9720
9730
|
* Replace `Function.prototype.toString` with one that recognizes
|
|
@@ -9726,7 +9736,10 @@ let markVirtualizedNativeFunction;
|
|
|
9726
9736
|
|
|
9727
9737
|
const tamingMethods = {
|
|
9728
9738
|
toString() {
|
|
9729
|
-
|
|
9739
|
+
let str = functionToString(this);
|
|
9740
|
+
// The embedder may map the text of source it rewrote back to the
|
|
9741
|
+
// text the page wrote (see the registered symbol below).
|
|
9742
|
+
if (sourceTextTransform !== undefined) str = sourceTextTransform(str);
|
|
9730
9743
|
if (
|
|
9731
9744
|
stringEndsWith(str, nativeSuffix) ||
|
|
9732
9745
|
!weaksetHas(virtualizedNativeFunctions, this)
|
|
@@ -9744,6 +9757,8 @@ let markVirtualizedNativeFunction;
|
|
|
9744
9757
|
markVirtualizedNativeFunction = freeze(func =>
|
|
9745
9758
|
weaksetAdd(virtualizedNativeFunctions, func),
|
|
9746
9759
|
);
|
|
9760
|
+
// Function.prototype.toString is itself a built-in function.
|
|
9761
|
+
markVirtualizedNativeFunction(tamingMethods.toString);
|
|
9747
9762
|
}
|
|
9748
9763
|
return markVirtualizedNativeFunction;
|
|
9749
9764
|
};$h͏_once.tameFunctionToString(tameFunctionToString);
|
|
@@ -12522,13 +12537,29 @@ const { quote: q } = assert;
|
|
|
12522
12537
|
|
|
12523
12538
|
|
|
12524
12539
|
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12540
|
+
// Evaluators for sloppyGlobalsMode / __sloppyCode__, one per compartment and
|
|
12541
|
+
// combination: building one scans the global object and compiles a new
|
|
12542
|
+
// evaluator function, which made every such evaluate() call cost hundreds of
|
|
12543
|
+
// microseconds.
|
|
12544
|
+
const sloppyEvaluators = new WeakMap();
|
|
12545
|
+
|
|
12546
|
+
const provideCompartmentEvaluator = (compartmentFields, options) => {
|
|
12547
|
+
const {
|
|
12548
|
+
sloppyGlobalsMode = false,
|
|
12549
|
+
__moduleShimLexicals__ = undefined,
|
|
12550
|
+
__sloppyCode__ = false,
|
|
12551
|
+
} = options;
|
|
12528
12552
|
|
|
12529
12553
|
let safeEvaluate;
|
|
12530
12554
|
|
|
12531
|
-
|
|
12555
|
+
const cacheKey = __moduleShimLexicals__ === undefined
|
|
12556
|
+
? `${Boolean(sloppyGlobalsMode)}:${Boolean(__sloppyCode__)}` : undefined;
|
|
12557
|
+
let cached = sloppyEvaluators.get(compartmentFields);
|
|
12558
|
+
if (cacheKey !== undefined && cached && cached.has(cacheKey)) {
|
|
12559
|
+
return { safeEvaluate: cached.get(cacheKey) };
|
|
12560
|
+
}
|
|
12561
|
+
|
|
12562
|
+
if (__moduleShimLexicals__ === undefined && !sloppyGlobalsMode && !__sloppyCode__) {
|
|
12532
12563
|
({ safeEvaluate } = compartmentFields);
|
|
12533
12564
|
} else {
|
|
12534
12565
|
// The scope proxy or global lexicals are different from the
|
|
@@ -12560,7 +12591,15 @@ const { quote: q } = assert;
|
|
|
12560
12591
|
moduleLexicals,
|
|
12561
12592
|
globalTransforms,
|
|
12562
12593
|
sloppyGlobalsMode,
|
|
12594
|
+
sloppyCode: __sloppyCode__,
|
|
12563
12595
|
}));
|
|
12596
|
+
if (cacheKey !== undefined) {
|
|
12597
|
+
if (!cached) {
|
|
12598
|
+
cached = new Map();
|
|
12599
|
+
sloppyEvaluators.set(compartmentFields, cached);
|
|
12600
|
+
}
|
|
12601
|
+
cached.set(cacheKey, safeEvaluate);
|
|
12602
|
+
}
|
|
12564
12603
|
}
|
|
12565
12604
|
|
|
12566
12605
|
return { safeEvaluate };
|
|
@@ -15138,6 +15177,13 @@ globalThis.repairIntrinsics = options => {
|
|
|
15138
15177
|
|
|
15139
15178
|
const markVirtualizedNativeFunction = tameFunctionToString();
|
|
15140
15179
|
|
|
15180
|
+
// The embedder takes this once (and deletes it) to present its own
|
|
15181
|
+
// host-implemented Web APIs as built-in functions, as browsers do.
|
|
15182
|
+
Object.defineProperty(globalThis, Symbol.for('ses.markVirtualizedNativeFunction'), {
|
|
15183
|
+
value: markVirtualizedNativeFunction,
|
|
15184
|
+
configurable: true,
|
|
15185
|
+
});
|
|
15186
|
+
|
|
15141
15187
|
const muteReporter = chooseReporter('none');
|
|
15142
15188
|
|
|
15143
15189
|
// @ts-ignore Compartment is definitely on globalThis.
|