@nextop-os/browser-node 0.0.27 → 0.0.29
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/dist/assets/workspace-dock-website.png +0 -0
- package/dist/{chunk-JRJTAIK5.js → chunk-G3H2RFWQ.js} +3 -1
- package/dist/chunk-G3H2RFWQ.js.map +1 -0
- package/dist/{chunk-AGQVWZYP.js → chunk-HBFCSUQO.js} +137 -34
- package/dist/chunk-HBFCSUQO.js.map +1 -0
- package/dist/{chunk-OTK5YBCK.js → chunk-UTXZLRPE.js} +11 -4
- package/dist/chunk-UTXZLRPE.js.map +1 -0
- package/dist/electron-main/index.d.ts +26 -5
- package/dist/electron-main/index.js +274 -47
- package/dist/electron-main/index.js.map +1 -1
- package/dist/electron-preload/index.d.ts +23 -1
- package/dist/electron-preload/index.js +112 -1
- package/dist/electron-preload/index.js.map +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +3 -3
- package/dist/react/index.d.ts +7 -3
- package/dist/react/index.js +3 -2
- package/dist/workbench/index.js +9 -3
- package/dist/workbench/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-AGQVWZYP.js.map +0 -1
- package/dist/chunk-JRJTAIK5.js.map +0 -1
- package/dist/chunk-OTK5YBCK.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BrowserWindow,
|
|
1
|
+
import { BrowserWindow, WebContents, WebPreferences } from 'electron';
|
|
2
2
|
|
|
3
3
|
interface BrowserNodeLoopbackPreviewTarget {
|
|
4
4
|
readonly targetUrl: string;
|
|
@@ -19,15 +19,23 @@ interface BrowserNodeLoopbackPreviewRoutingOptions {
|
|
|
19
19
|
type BrowserPreferredColorScheme = "dark" | "light";
|
|
20
20
|
interface BrowserNodeElectronLogger {
|
|
21
21
|
debug?(message: string, metadata?: Record<string, unknown>): void;
|
|
22
|
+
info?(message: string, metadata?: Record<string, unknown>): void;
|
|
22
23
|
warn?(message: string, metadata?: Record<string, unknown>): void;
|
|
23
24
|
}
|
|
24
25
|
interface BrowserGuestWebContents {
|
|
25
26
|
readonly id?: number;
|
|
27
|
+
readonly navigationHistory?: {
|
|
28
|
+
canGoBack(): boolean;
|
|
29
|
+
canGoForward(): boolean;
|
|
30
|
+
goBack(): void;
|
|
31
|
+
goForward(): void;
|
|
32
|
+
};
|
|
26
33
|
canGoBack(): boolean;
|
|
27
34
|
canGoForward(): boolean;
|
|
28
35
|
capturePage?(): Promise<BrowserGuestNativeImage>;
|
|
29
36
|
getTitle(): string;
|
|
30
37
|
getURL(): string;
|
|
38
|
+
getUserAgent?(): string;
|
|
31
39
|
goBack(): void;
|
|
32
40
|
goForward(): void;
|
|
33
41
|
isDestroyed(): boolean;
|
|
@@ -36,6 +44,7 @@ interface BrowserGuestWebContents {
|
|
|
36
44
|
off(event: string, listener: (...args: unknown[]) => void): this;
|
|
37
45
|
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
38
46
|
reload(): void;
|
|
47
|
+
setUserAgent?(userAgent: string): void;
|
|
39
48
|
setWindowOpenHandler?(handler: (details: {
|
|
40
49
|
url: string;
|
|
41
50
|
}) => {
|
|
@@ -64,7 +73,9 @@ interface BrowserNodeElectronMainChannels {
|
|
|
64
73
|
readonly event: string;
|
|
65
74
|
readonly goBack: string;
|
|
66
75
|
readonly goForward: string;
|
|
76
|
+
readonly guestOpenUrl?: string;
|
|
67
77
|
readonly navigate: string;
|
|
78
|
+
readonly openExternal?: string;
|
|
68
79
|
readonly prepareSession: string;
|
|
69
80
|
readonly registerGuest: string;
|
|
70
81
|
readonly reload: string;
|
|
@@ -78,6 +89,7 @@ interface RegisterBrowserNodeElectronMainInput {
|
|
|
78
89
|
readonly loopbackPreviewRouting?: BrowserNodeLoopbackPreviewRoutingOptions;
|
|
79
90
|
readonly openExternal: (url: string) => Promise<void> | void;
|
|
80
91
|
readonly registerHandler: <TPayload, TResult>(channel: string, handler: (event: unknown, payload: TPayload) => Promise<TResult> | TResult) => void;
|
|
92
|
+
readonly registerListener?: <TPayload>(channel: string, handler: (event: unknown, payload: TPayload) => void) => void;
|
|
81
93
|
readonly resolveWebContents: (input: {
|
|
82
94
|
event: unknown;
|
|
83
95
|
ownerWindow: BrowserWindow;
|
|
@@ -88,7 +100,15 @@ interface RegisterBrowserNodeElectronMainInput {
|
|
|
88
100
|
}
|
|
89
101
|
declare function registerBrowserNodeElectronMain(input: RegisterBrowserNodeElectronMainInput): void;
|
|
90
102
|
|
|
103
|
+
declare function sanitizeBrowserGuestUserAgent(userAgent: string): string;
|
|
104
|
+
declare function applyBrowserGuestUserAgent(contents: WebContents, logger?: BrowserNodeElectronLogger): void;
|
|
105
|
+
|
|
106
|
+
interface BrowserSessionPartitionAllowedOptions {
|
|
107
|
+
additionalAllowedPrefixes?: readonly string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
91
110
|
interface BrowserWebviewSecurityInput {
|
|
111
|
+
allowedSessionPartitions?: BrowserSessionPartitionAllowedOptions;
|
|
92
112
|
params: Record<string, string>;
|
|
93
113
|
resolvePreload?: BrowserWebviewPreloadResolver;
|
|
94
114
|
webPreferences: WebPreferences;
|
|
@@ -102,9 +122,10 @@ interface BrowserWebviewPreloadResolverInput {
|
|
|
102
122
|
params: Readonly<Record<string, string>>;
|
|
103
123
|
}
|
|
104
124
|
type BrowserWebviewPreloadResolver = (input: BrowserWebviewPreloadResolverInput) => string | null | undefined;
|
|
105
|
-
declare function isBrowserNodeWebviewAttach(params: Record<string, string
|
|
106
|
-
declare function enforceBrowserWebviewSecurity({ params, resolvePreload, webPreferences }: BrowserWebviewSecurityInput): BrowserWebviewSecurityResult;
|
|
125
|
+
declare function isBrowserNodeWebviewAttach(params: Record<string, string>, allowedSessionPartitions?: BrowserSessionPartitionAllowedOptions): boolean;
|
|
126
|
+
declare function enforceBrowserWebviewSecurity({ allowedSessionPartitions, params, resolvePreload, webPreferences }: BrowserWebviewSecurityInput): BrowserWebviewSecurityResult;
|
|
107
127
|
interface InstallBrowserWebviewSecurityInput {
|
|
128
|
+
allowedSessionPartitions?: BrowserSessionPartitionAllowedOptions;
|
|
108
129
|
contents: WebContents;
|
|
109
130
|
logger?: BrowserNodeElectronLogger;
|
|
110
131
|
onGuestAttached?: (guestContents: WebContents) => void;
|
|
@@ -112,6 +133,6 @@ interface InstallBrowserWebviewSecurityInput {
|
|
|
112
133
|
resolvePreload?: BrowserWebviewPreloadResolver;
|
|
113
134
|
shouldHandleWebview?: BrowserNodeWebviewMatcher;
|
|
114
135
|
}
|
|
115
|
-
declare function installBrowserWebviewSecurity({ contents, logger, onGuestAttached, openExternal, resolvePreload, shouldHandleWebview }: InstallBrowserWebviewSecurityInput): () => void;
|
|
136
|
+
declare function installBrowserWebviewSecurity({ allowedSessionPartitions, contents, logger, onGuestAttached, openExternal, resolvePreload, shouldHandleWebview }: InstallBrowserWebviewSecurityInput): () => void;
|
|
116
137
|
|
|
117
|
-
export { type BrowserNodeElectronLogger, type BrowserNodeElectronMainChannels, type BrowserNodeLoopbackPreviewResolver, type BrowserNodeLoopbackPreviewRoutingOptions, type BrowserNodeLoopbackPreviewTarget, type BrowserNodeWebviewMatcher, type BrowserWebviewPreloadResolver, type BrowserWebviewPreloadResolverInput, type BrowserWebviewSecurityInput, type BrowserWebviewSecurityResult, type InstallBrowserWebviewSecurityInput, type RegisterBrowserNodeElectronMainInput, enforceBrowserWebviewSecurity, installBrowserWebviewSecurity, isBrowserNodeWebviewAttach, registerBrowserNodeElectronMain };
|
|
138
|
+
export { type BrowserNodeElectronLogger, type BrowserNodeElectronMainChannels, type BrowserNodeLoopbackPreviewResolver, type BrowserNodeLoopbackPreviewRoutingOptions, type BrowserNodeLoopbackPreviewTarget, type BrowserNodeWebviewMatcher, type BrowserWebviewPreloadResolver, type BrowserWebviewPreloadResolverInput, type BrowserWebviewSecurityInput, type BrowserWebviewSecurityResult, type InstallBrowserWebviewSecurityInput, type RegisterBrowserNodeElectronMainInput, applyBrowserGuestUserAgent, enforceBrowserWebviewSecurity, installBrowserWebviewSecurity, isBrowserNodeWebviewAttach, registerBrowserNodeElectronMain, sanitizeBrowserGuestUserAgent };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBrowserSessionPartitionAllowed,
|
|
3
|
+
resolveBrowserSessionPartition
|
|
4
|
+
} from "../chunk-UTXZLRPE.js";
|
|
1
5
|
import {
|
|
2
6
|
normalizeBrowserComparableUrl,
|
|
3
7
|
resolveBrowserNavigationUrl
|
|
4
8
|
} from "../chunk-2ZAMKGGP.js";
|
|
5
|
-
import {
|
|
6
|
-
isBrowserSessionPartitionAllowed,
|
|
7
|
-
resolveBrowserSessionPartition
|
|
8
|
-
} from "../chunk-OTK5YBCK.js";
|
|
9
9
|
|
|
10
10
|
// src/electron-main/loopbackPreviewProxy.ts
|
|
11
11
|
import {
|
|
@@ -13,12 +13,8 @@ import {
|
|
|
13
13
|
request as httpRequest
|
|
14
14
|
} from "http";
|
|
15
15
|
import { request as httpsRequest } from "https";
|
|
16
|
-
import { createRequire } from "module";
|
|
17
16
|
import { pipeline } from "stream";
|
|
18
|
-
|
|
19
|
-
var wsModule = require2("ws");
|
|
20
|
-
var WebSocket = wsModule;
|
|
21
|
-
var { WebSocketServer } = wsModule;
|
|
17
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
22
18
|
var defaultLoopbackPreviewCacheTtlMs = 3e4;
|
|
23
19
|
var loopbackHostPattern = /^(localhost|127(?:\.\d{1,3}){0,3})$/i;
|
|
24
20
|
var loopbackPreviewProxyBypassRules = "*;<-loopback>;https://*;wss://*";
|
|
@@ -463,6 +459,26 @@ function normalizeWebSocketCloseCode(code) {
|
|
|
463
459
|
var browserPreviewMaxWidth = 260;
|
|
464
460
|
var browserPreviewMaxHeight = 170;
|
|
465
461
|
var abortedNavigationErrorCode = -3;
|
|
462
|
+
function canGuestGoBack(contents) {
|
|
463
|
+
return contents.navigationHistory?.canGoBack() ?? contents.canGoBack();
|
|
464
|
+
}
|
|
465
|
+
function canGuestGoForward(contents) {
|
|
466
|
+
return contents.navigationHistory?.canGoForward() ?? contents.canGoForward();
|
|
467
|
+
}
|
|
468
|
+
function goGuestBack(contents) {
|
|
469
|
+
if (contents.navigationHistory) {
|
|
470
|
+
contents.navigationHistory.goBack();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
contents.goBack();
|
|
474
|
+
}
|
|
475
|
+
function goGuestForward(contents) {
|
|
476
|
+
if (contents.navigationHistory) {
|
|
477
|
+
contents.navigationHistory.goForward();
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
contents.goForward();
|
|
481
|
+
}
|
|
466
482
|
function resolveBrowserNodeUrlError(resolved) {
|
|
467
483
|
if (resolved.errorCode === "invalid-url") {
|
|
468
484
|
return { code: "invalid-url" };
|
|
@@ -500,6 +516,28 @@ function resizeBrowserPreviewImage(image) {
|
|
|
500
516
|
function isAbortedNavigationError(input) {
|
|
501
517
|
return input.errorCode === abortedNavigationErrorCode || input.errorDescription === "ERR_ABORTED";
|
|
502
518
|
}
|
|
519
|
+
function resolveBrowserNavigationOrigin(url) {
|
|
520
|
+
const resolved = resolveBrowserNavigationUrl(url);
|
|
521
|
+
if (!resolved.url) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
return new URL(resolved.url).origin;
|
|
526
|
+
} catch {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function isBrowserNavigationAllowedByPolicy(input) {
|
|
531
|
+
if (!input.policy) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (input.policy.mode === "same-origin") {
|
|
535
|
+
const policyOrigin = resolveBrowserNavigationOrigin(input.policy.originUrl);
|
|
536
|
+
const nextOrigin = resolveBrowserNavigationOrigin(input.url);
|
|
537
|
+
return policyOrigin !== null && nextOrigin !== null && policyOrigin === nextOrigin;
|
|
538
|
+
}
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
503
541
|
async function applyPreferredColorSchemeToGuest(session, logger, syncPreferredColorScheme, scheme) {
|
|
504
542
|
const contents = session.contents;
|
|
505
543
|
if (!contents || contents.isDestroyed() || !syncPreferredColorScheme || scheme === null || session.appliedColorScheme === scheme) {
|
|
@@ -540,6 +578,12 @@ function createBrowserGuestManager({
|
|
|
540
578
|
if (input?.sessionMode !== void 0) {
|
|
541
579
|
existing.sessionMode = input.sessionMode;
|
|
542
580
|
}
|
|
581
|
+
if (input?.sessionPartition !== void 0) {
|
|
582
|
+
existing.sessionPartition = input.sessionPartition;
|
|
583
|
+
}
|
|
584
|
+
if (input?.navigationPolicy !== void 0) {
|
|
585
|
+
existing.navigationPolicy = input.navigationPolicy;
|
|
586
|
+
}
|
|
543
587
|
if (input?.url !== void 0) {
|
|
544
588
|
existing.desiredUrl = input.url;
|
|
545
589
|
}
|
|
@@ -551,9 +595,11 @@ function createBrowserGuestManager({
|
|
|
551
595
|
desiredUrl: input?.url ?? "about:blank",
|
|
552
596
|
lifecycle: "cold",
|
|
553
597
|
listeners: [],
|
|
598
|
+
navigationPolicy: input?.navigationPolicy ?? null,
|
|
554
599
|
nodeId,
|
|
555
600
|
profileId: input?.profileId ?? null,
|
|
556
601
|
sessionMode: input?.sessionMode ?? "shared",
|
|
602
|
+
sessionPartition: input?.sessionPartition ?? null,
|
|
557
603
|
webContentsId: null
|
|
558
604
|
};
|
|
559
605
|
sessions.set(nodeId, session);
|
|
@@ -562,8 +608,8 @@ function createBrowserGuestManager({
|
|
|
562
608
|
const publishState = (session) => {
|
|
563
609
|
const contents = session.contents && !session.contents.isDestroyed() ? session.contents : null;
|
|
564
610
|
emit({
|
|
565
|
-
canGoBack: contents ? contents
|
|
566
|
-
canGoForward: contents ? contents
|
|
611
|
+
canGoBack: contents ? canGuestGoBack(contents) : false,
|
|
612
|
+
canGoForward: contents ? canGuestGoForward(contents) : false,
|
|
567
613
|
isAttachedToWindow: Boolean(contents),
|
|
568
614
|
isLoading: contents ? contents.isLoading() : false,
|
|
569
615
|
isOccluded: session.lifecycle === "cold",
|
|
@@ -613,10 +659,22 @@ function createBrowserGuestManager({
|
|
|
613
659
|
const onFailLoad = (...args) => {
|
|
614
660
|
const errorCode = typeof args[1] === "number" ? args[1] : void 0;
|
|
615
661
|
const errorDescription = typeof args[2] === "string" ? args[2] : void 0;
|
|
662
|
+
const validatedUrl = typeof args[3] === "string" ? args[3] : void 0;
|
|
663
|
+
const isMainFrame = typeof args[4] === "boolean" ? args[4] : void 0;
|
|
616
664
|
if (isAbortedNavigationError({ errorCode, errorDescription })) {
|
|
617
665
|
publishState(session);
|
|
618
666
|
return;
|
|
619
667
|
}
|
|
668
|
+
logger?.warn?.("Browser Node guest navigation failed", {
|
|
669
|
+
currentUrl: contents.getURL(),
|
|
670
|
+
desiredUrl: session.desiredUrl,
|
|
671
|
+
errorCode,
|
|
672
|
+
errorDescription,
|
|
673
|
+
isMainFrame,
|
|
674
|
+
nodeId: session.nodeId,
|
|
675
|
+
validatedUrl,
|
|
676
|
+
webContentsId: session.webContentsId
|
|
677
|
+
});
|
|
620
678
|
emit({
|
|
621
679
|
code: "navigation-failed",
|
|
622
680
|
diagnosticMessage: errorDescription,
|
|
@@ -627,12 +685,26 @@ function createBrowserGuestManager({
|
|
|
627
685
|
publishState(session);
|
|
628
686
|
};
|
|
629
687
|
const onDestroyed = () => detachGuest(session);
|
|
688
|
+
const onWillNavigate = (...args) => {
|
|
689
|
+
const event = args[0] && typeof args[0] === "object" && "preventDefault" in args[0] ? args[0] : null;
|
|
690
|
+
const url = typeof args[1] === "string" ? args[1] : "";
|
|
691
|
+
if (url.length === 0 || isBrowserNavigationAllowedByPolicy({
|
|
692
|
+
policy: session.navigationPolicy,
|
|
693
|
+
url
|
|
694
|
+
})) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
event?.preventDefault?.();
|
|
698
|
+
emitOpenUrlFromGuest(session, url);
|
|
699
|
+
publishState(session);
|
|
700
|
+
};
|
|
630
701
|
const records = [
|
|
631
702
|
{ event: "did-start-loading", listener: onStateChange },
|
|
632
703
|
{ event: "did-stop-loading", listener: onStateChange },
|
|
633
704
|
{ event: "did-navigate", listener: onStateChange },
|
|
634
705
|
{ event: "did-navigate-in-page", listener: onStateChange },
|
|
635
706
|
{ event: "page-title-updated", listener: onStateChange },
|
|
707
|
+
{ event: "will-navigate", listener: onWillNavigate },
|
|
636
708
|
{ event: "did-fail-load", listener: onFailLoad },
|
|
637
709
|
{ event: "destroyed", listener: onDestroyed }
|
|
638
710
|
];
|
|
@@ -657,26 +729,57 @@ function createBrowserGuestManager({
|
|
|
657
729
|
publishState(session);
|
|
658
730
|
return;
|
|
659
731
|
}
|
|
732
|
+
if (!isBrowserNavigationAllowedByPolicy({
|
|
733
|
+
policy: session.navigationPolicy,
|
|
734
|
+
url: resolved.url
|
|
735
|
+
})) {
|
|
736
|
+
emitOpenUrlFromGuest(session, resolved.url);
|
|
737
|
+
publishState(session);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
660
740
|
const currentComparable = normalizeBrowserComparableUrl(contents.getURL());
|
|
661
741
|
const nextComparable = normalizeBrowserComparableUrl(resolved.url);
|
|
662
742
|
if (currentComparable && currentComparable === nextComparable) {
|
|
663
743
|
publishState(session);
|
|
664
744
|
return;
|
|
665
745
|
}
|
|
666
|
-
|
|
667
|
-
|
|
746
|
+
try {
|
|
747
|
+
await contents.loadURL(resolved.url);
|
|
748
|
+
} catch (error) {
|
|
749
|
+
logger?.warn?.("Browser Node guest loadURL failed", {
|
|
750
|
+
currentUrl: contents.getURL(),
|
|
751
|
+
desiredUrl: session.desiredUrl,
|
|
752
|
+
error: error instanceof Error ? error.message : String(error),
|
|
753
|
+
nodeId: session.nodeId,
|
|
754
|
+
resolvedUrl: resolved.url,
|
|
755
|
+
webContentsId: session.webContentsId
|
|
756
|
+
});
|
|
757
|
+
throw error;
|
|
758
|
+
} finally {
|
|
759
|
+
publishState(session);
|
|
760
|
+
}
|
|
668
761
|
};
|
|
669
|
-
const
|
|
762
|
+
const emitOpenUrlFromGuest = (session, url) => {
|
|
670
763
|
const resolved = resolveBrowserNavigationUrl(url);
|
|
671
764
|
if (resolved.url) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
765
|
+
logger?.info?.("Browser Node guest emitted open-url", {
|
|
766
|
+
nodeId: session.nodeId,
|
|
767
|
+
url: resolved.url,
|
|
768
|
+
webContentsId: session.webContentsId
|
|
769
|
+
});
|
|
770
|
+
emit({
|
|
771
|
+
reuseIfOpen: false,
|
|
772
|
+
sourceNodeId: session.nodeId,
|
|
773
|
+
type: "open-url",
|
|
774
|
+
url: resolved.url
|
|
775
|
+
});
|
|
776
|
+
return { action: "deny" };
|
|
679
777
|
}
|
|
778
|
+
void Promise.resolve(openExternal(url)).catch((error) => {
|
|
779
|
+
logger?.warn?.("Browser Node openExternal failed", {
|
|
780
|
+
error: error instanceof Error ? error.message : String(error)
|
|
781
|
+
});
|
|
782
|
+
});
|
|
680
783
|
return { action: "deny" };
|
|
681
784
|
};
|
|
682
785
|
return {
|
|
@@ -686,8 +789,10 @@ function createBrowserGuestManager({
|
|
|
686
789
|
throw new Error("Browser Node rejected navigation URL");
|
|
687
790
|
}
|
|
688
791
|
const session = getSession(input.nodeId, {
|
|
792
|
+
navigationPolicy: input.navigationPolicy,
|
|
689
793
|
profileId: input.profileId,
|
|
690
794
|
sessionMode: input.sessionMode,
|
|
795
|
+
sessionPartition: input.sessionPartition,
|
|
691
796
|
url: resolved.url
|
|
692
797
|
});
|
|
693
798
|
session.lifecycle = "active";
|
|
@@ -721,8 +826,8 @@ function createBrowserGuestManager({
|
|
|
721
826
|
}
|
|
722
827
|
const contents = session.contents && !session.contents.isDestroyed() ? session.contents : null;
|
|
723
828
|
return {
|
|
724
|
-
canGoBack: contents ? contents
|
|
725
|
-
canGoForward: contents ? contents
|
|
829
|
+
canGoBack: contents ? canGuestGoBack(contents) : false,
|
|
830
|
+
canGoForward: contents ? canGuestGoForward(contents) : false,
|
|
726
831
|
currentUrl: contents ? contents.getURL() : null,
|
|
727
832
|
desiredUrl: session.desiredUrl,
|
|
728
833
|
isAttachedToWindow: Boolean(contents),
|
|
@@ -731,46 +836,79 @@ function createBrowserGuestManager({
|
|
|
731
836
|
nodeId: session.nodeId,
|
|
732
837
|
profileId: session.profileId,
|
|
733
838
|
sessionMode: session.sessionMode,
|
|
839
|
+
sessionPartition: session.sessionPartition,
|
|
734
840
|
title: contents ? contents.getTitle() : null,
|
|
841
|
+
userAgent: contents?.getUserAgent?.() ?? null,
|
|
735
842
|
webContentsDestroyed: session.contents ? session.contents.isDestroyed() : null,
|
|
736
843
|
webContentsId: session.webContentsId
|
|
737
844
|
};
|
|
738
845
|
},
|
|
739
846
|
goBack(input) {
|
|
740
847
|
const contents = sessions.get(input.nodeId)?.contents;
|
|
741
|
-
if (contents && !contents.isDestroyed() && contents
|
|
742
|
-
contents
|
|
848
|
+
if (contents && !contents.isDestroyed() && canGuestGoBack(contents)) {
|
|
849
|
+
goGuestBack(contents);
|
|
743
850
|
}
|
|
744
851
|
return Promise.resolve();
|
|
745
852
|
},
|
|
746
853
|
goForward(input) {
|
|
747
854
|
const contents = sessions.get(input.nodeId)?.contents;
|
|
748
|
-
if (contents && !contents.isDestroyed() && contents
|
|
749
|
-
contents
|
|
855
|
+
if (contents && !contents.isDestroyed() && canGuestGoForward(contents)) {
|
|
856
|
+
goGuestForward(contents);
|
|
750
857
|
}
|
|
751
858
|
return Promise.resolve();
|
|
752
859
|
},
|
|
860
|
+
handleGuestOpenUrl(webContentsId, input) {
|
|
861
|
+
const nodeId = nodeIdByWebContentsId.get(webContentsId);
|
|
862
|
+
const session = nodeId ? sessions.get(nodeId) : null;
|
|
863
|
+
if (!session) {
|
|
864
|
+
logger?.warn?.("Browser Node ignored guest open-url request", {
|
|
865
|
+
url: input.url,
|
|
866
|
+
webContentsId
|
|
867
|
+
});
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
logger?.info?.("Browser Node handling guest open-url request", {
|
|
871
|
+
nodeId: session.nodeId,
|
|
872
|
+
url: input.url,
|
|
873
|
+
webContentsId
|
|
874
|
+
});
|
|
875
|
+
emitOpenUrlFromGuest(session, input.url);
|
|
876
|
+
},
|
|
753
877
|
async navigate(input) {
|
|
754
878
|
const resolved = resolveBrowserNavigationUrl(input.url);
|
|
755
879
|
if (!resolved.url) {
|
|
756
880
|
throw new Error("Browser Node rejected navigation URL");
|
|
757
881
|
}
|
|
758
|
-
const session = getSession(input.nodeId, {
|
|
882
|
+
const session = getSession(input.nodeId, {
|
|
883
|
+
navigationPolicy: input.navigationPolicy,
|
|
884
|
+
url: resolved.url
|
|
885
|
+
});
|
|
759
886
|
session.lifecycle = "active";
|
|
760
887
|
await loadDesiredUrl(session);
|
|
761
888
|
},
|
|
889
|
+
async openExternal(input) {
|
|
890
|
+
const resolved = resolveBrowserNavigationUrl(input.url);
|
|
891
|
+
if (!resolved.url) {
|
|
892
|
+
throw new Error("Browser Node rejected external URL");
|
|
893
|
+
}
|
|
894
|
+
await Promise.resolve(openExternal(resolved.url));
|
|
895
|
+
},
|
|
762
896
|
async prepareSession(input) {
|
|
763
897
|
await prepareSession?.(input);
|
|
764
898
|
getSession(input.nodeId, {
|
|
899
|
+
navigationPolicy: input.navigationPolicy,
|
|
765
900
|
profileId: input.profileId,
|
|
766
|
-
sessionMode: input.sessionMode
|
|
901
|
+
sessionMode: input.sessionMode,
|
|
902
|
+
sessionPartition: input.sessionPartition
|
|
767
903
|
});
|
|
768
904
|
},
|
|
769
905
|
async registerGuest(input) {
|
|
770
906
|
await prepareSession?.({
|
|
771
907
|
nodeId: input.nodeId,
|
|
772
908
|
profileId: input.profileId,
|
|
773
|
-
sessionMode: input.sessionMode
|
|
909
|
+
sessionMode: input.sessionMode,
|
|
910
|
+
sessionPartition: input.sessionPartition,
|
|
911
|
+
navigationPolicy: input.navigationPolicy
|
|
774
912
|
});
|
|
775
913
|
const contents = resolveWebContents(input.webContentsId);
|
|
776
914
|
if (!contents || contents.isDestroyed()) {
|
|
@@ -785,8 +923,10 @@ function createBrowserGuestManager({
|
|
|
785
923
|
);
|
|
786
924
|
}
|
|
787
925
|
const session = getSession(input.nodeId, {
|
|
926
|
+
navigationPolicy: input.navigationPolicy,
|
|
788
927
|
profileId: input.profileId,
|
|
789
|
-
sessionMode: input.sessionMode
|
|
928
|
+
sessionMode: input.sessionMode,
|
|
929
|
+
sessionPartition: input.sessionPartition
|
|
790
930
|
});
|
|
791
931
|
if (session.webContentsId === input.webContentsId && session.contents === contents) {
|
|
792
932
|
publishState(session);
|
|
@@ -799,7 +939,14 @@ function createBrowserGuestManager({
|
|
|
799
939
|
session.webContentsId = input.webContentsId;
|
|
800
940
|
nodeIdByWebContentsId.set(input.webContentsId, input.nodeId);
|
|
801
941
|
session.lifecycle = "active";
|
|
802
|
-
|
|
942
|
+
logger?.info?.("Browser Node registered guest owner", {
|
|
943
|
+
nodeId: input.nodeId,
|
|
944
|
+
sessionPartition: session.sessionPartition,
|
|
945
|
+
webContentsId: input.webContentsId
|
|
946
|
+
});
|
|
947
|
+
contents.setWindowOpenHandler?.(
|
|
948
|
+
({ url }) => emitOpenUrlFromGuest(session, url)
|
|
949
|
+
);
|
|
803
950
|
attachGuestListeners(session);
|
|
804
951
|
await applyPreferredColorSchemeToGuest(
|
|
805
952
|
session,
|
|
@@ -836,12 +983,17 @@ function registerBrowserNodeElectronMain(input) {
|
|
|
836
983
|
const managersByWindow = /* @__PURE__ */ new WeakMap();
|
|
837
984
|
const loopbackPreviewProxy = input.loopbackPreviewRouting !== void 0 ? createBrowserNodeLoopbackPreviewProxy({
|
|
838
985
|
logger: input.logger,
|
|
839
|
-
resolveSession: async ({
|
|
986
|
+
resolveSession: async ({
|
|
987
|
+
profileId,
|
|
988
|
+
sessionMode,
|
|
989
|
+
sessionPartition
|
|
990
|
+
}) => {
|
|
840
991
|
const { session } = await import("electron");
|
|
841
992
|
return session.fromPartition(
|
|
842
993
|
resolveBrowserSessionPartition({
|
|
843
994
|
profileId,
|
|
844
|
-
sessionMode
|
|
995
|
+
sessionMode,
|
|
996
|
+
sessionPartition
|
|
845
997
|
})
|
|
846
998
|
);
|
|
847
999
|
},
|
|
@@ -913,6 +1065,14 @@ function registerBrowserNodeElectronMain(input) {
|
|
|
913
1065
|
input.channels.navigate,
|
|
914
1066
|
(event, payload) => resolveManager(event).navigate(payload)
|
|
915
1067
|
);
|
|
1068
|
+
if (input.channels.openExternal) {
|
|
1069
|
+
input.registerHandler(
|
|
1070
|
+
input.channels.openExternal,
|
|
1071
|
+
(event, payload) => resolveManager(event).openExternal(
|
|
1072
|
+
payload
|
|
1073
|
+
)
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
916
1076
|
input.registerHandler(
|
|
917
1077
|
input.channels.goBack,
|
|
918
1078
|
(event, payload) => resolveManager(event).goBack(payload)
|
|
@@ -935,13 +1095,52 @@ function registerBrowserNodeElectronMain(input) {
|
|
|
935
1095
|
(event, payload) => resolveManager(event).debugDump(payload)
|
|
936
1096
|
);
|
|
937
1097
|
}
|
|
1098
|
+
if (input.channels.guestOpenUrl && input.registerListener) {
|
|
1099
|
+
input.registerListener(input.channels.guestOpenUrl, (event, payload) => {
|
|
1100
|
+
const senderId = readBrowserNodeIpcSenderId(event);
|
|
1101
|
+
const openUrlPayload = payload;
|
|
1102
|
+
if (typeof senderId !== "number" || !Number.isFinite(senderId) || !openUrlPayload || typeof openUrlPayload.url !== "string") {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
resolveManager(event).handleGuestOpenUrl(senderId, openUrlPayload);
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
function readBrowserNodeIpcSenderId(event) {
|
|
1110
|
+
const sender = event?.sender;
|
|
1111
|
+
return typeof sender?.id === "number" ? sender.id : null;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/electron-main/userAgent.ts
|
|
1115
|
+
var electronUserAgentTokenPattern = /\sElectron\/[^\s]+/g;
|
|
1116
|
+
function sanitizeBrowserGuestUserAgent(userAgent) {
|
|
1117
|
+
return userAgent.trim().replace(electronUserAgentTokenPattern, "").replace(/\s{2,}/g, " ");
|
|
1118
|
+
}
|
|
1119
|
+
function applyBrowserGuestUserAgent(contents, logger) {
|
|
1120
|
+
const guestContents = contents;
|
|
1121
|
+
if (typeof guestContents.getUserAgent !== "function" || typeof guestContents.setUserAgent !== "function") {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
const currentUserAgent = guestContents.getUserAgent().trim();
|
|
1125
|
+
const nextUserAgent = sanitizeBrowserGuestUserAgent(currentUserAgent);
|
|
1126
|
+
if (!nextUserAgent || nextUserAgent === currentUserAgent) {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
guestContents.setUserAgent(nextUserAgent);
|
|
1130
|
+
logger?.debug?.("Browser Node sanitized guest user agent", {
|
|
1131
|
+
webContentsId: contents.id ?? null
|
|
1132
|
+
});
|
|
938
1133
|
}
|
|
939
1134
|
|
|
940
1135
|
// src/electron-main/webviewSecurity.ts
|
|
941
|
-
function
|
|
942
|
-
return
|
|
1136
|
+
function isBrowserNodeInitialWebviewUrl(url) {
|
|
1137
|
+
return (url ?? "").trim() === "about:blank";
|
|
1138
|
+
}
|
|
1139
|
+
function isBrowserNodeWebviewAttach(params, allowedSessionPartitions) {
|
|
1140
|
+
return params["data-browser-node-webview"] === "true" || isBrowserSessionPartitionAllowed(params.partition, allowedSessionPartitions);
|
|
943
1141
|
}
|
|
944
1142
|
function enforceBrowserWebviewSecurity({
|
|
1143
|
+
allowedSessionPartitions,
|
|
945
1144
|
params,
|
|
946
1145
|
resolvePreload,
|
|
947
1146
|
webPreferences
|
|
@@ -955,20 +1154,24 @@ function enforceBrowserWebviewSecurity({
|
|
|
955
1154
|
webPreferences.webSecurity = true;
|
|
956
1155
|
delete webPreferences.preload;
|
|
957
1156
|
const partition = params.partition;
|
|
958
|
-
if (!partition || !isBrowserSessionPartitionAllowed(partition)) {
|
|
1157
|
+
if (!partition || !isBrowserSessionPartitionAllowed(partition, allowedSessionPartitions)) {
|
|
959
1158
|
return {
|
|
960
1159
|
allowed: false,
|
|
961
1160
|
reason: "Unsupported Browser Node session partition"
|
|
962
1161
|
};
|
|
963
1162
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1163
|
+
if (isBrowserNodeInitialWebviewUrl(params.src)) {
|
|
1164
|
+
params.src = "about:blank";
|
|
1165
|
+
} else {
|
|
1166
|
+
const resolved = resolveBrowserNavigationUrl(params.src ?? "about:blank");
|
|
1167
|
+
if (!resolved.url) {
|
|
1168
|
+
return {
|
|
1169
|
+
allowed: false,
|
|
1170
|
+
reason: "Unsupported browser URL"
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
params.src = resolved.url;
|
|
970
1174
|
}
|
|
971
|
-
params.src = resolved.url;
|
|
972
1175
|
const preload = resolvePreload?.({ params: { ...params } });
|
|
973
1176
|
const resolvedPreload = typeof preload === "string" ? preload.trim() : "";
|
|
974
1177
|
if (resolvedPreload.length > 0) {
|
|
@@ -977,20 +1180,28 @@ function enforceBrowserWebviewSecurity({
|
|
|
977
1180
|
return { allowed: true, reason: null };
|
|
978
1181
|
}
|
|
979
1182
|
function installBrowserWebviewSecurity({
|
|
1183
|
+
allowedSessionPartitions,
|
|
980
1184
|
contents,
|
|
981
1185
|
logger,
|
|
982
1186
|
onGuestAttached,
|
|
983
1187
|
openExternal,
|
|
984
1188
|
resolvePreload,
|
|
985
|
-
shouldHandleWebview
|
|
1189
|
+
shouldHandleWebview
|
|
986
1190
|
}) {
|
|
987
1191
|
let pendingBrowserAttachCount = 0;
|
|
988
1192
|
const handleWillAttachWebview = (event, webPreferences, params) => {
|
|
989
|
-
|
|
1193
|
+
const shouldHandle = shouldHandleWebview?.(params) ?? isBrowserNodeWebviewAttach(params, allowedSessionPartitions);
|
|
1194
|
+
logger?.debug?.("Browser Node webview will attach", {
|
|
1195
|
+
partition: params.partition ?? null,
|
|
1196
|
+
shouldHandle,
|
|
1197
|
+
src: params.src ?? null
|
|
1198
|
+
});
|
|
1199
|
+
if (!shouldHandle) {
|
|
990
1200
|
return;
|
|
991
1201
|
}
|
|
992
1202
|
pendingBrowserAttachCount += 1;
|
|
993
1203
|
const result = enforceBrowserWebviewSecurity({
|
|
1204
|
+
allowedSessionPartitions,
|
|
994
1205
|
params,
|
|
995
1206
|
resolvePreload,
|
|
996
1207
|
webPreferences
|
|
@@ -999,14 +1210,28 @@ function installBrowserWebviewSecurity({
|
|
|
999
1210
|
pendingBrowserAttachCount = Math.max(0, pendingBrowserAttachCount - 1);
|
|
1000
1211
|
logger?.warn?.("Browser Node webview blocked", { reason: result.reason });
|
|
1001
1212
|
event.preventDefault();
|
|
1213
|
+
return;
|
|
1002
1214
|
}
|
|
1215
|
+
logger?.debug?.("Browser Node webview attach allowed", {
|
|
1216
|
+
partition: params.partition ?? null,
|
|
1217
|
+
src: params.src ?? null
|
|
1218
|
+
});
|
|
1003
1219
|
};
|
|
1004
1220
|
const handleDidAttachWebview = (_event, guestContents) => {
|
|
1005
1221
|
if (pendingBrowserAttachCount <= 0) {
|
|
1222
|
+
logger?.debug?.("Browser Node webview did attach ignored", {
|
|
1223
|
+
guestWebContentsId: guestContents.id ?? null,
|
|
1224
|
+
pendingBrowserAttachCount
|
|
1225
|
+
});
|
|
1006
1226
|
return;
|
|
1007
1227
|
}
|
|
1008
1228
|
pendingBrowserAttachCount -= 1;
|
|
1229
|
+
applyBrowserGuestUserAgent(guestContents, logger);
|
|
1009
1230
|
onGuestAttached?.(guestContents);
|
|
1231
|
+
logger?.debug?.("Browser Node webview guest attached", {
|
|
1232
|
+
guestWebContentsId: guestContents.id ?? null,
|
|
1233
|
+
pendingBrowserAttachCount
|
|
1234
|
+
});
|
|
1010
1235
|
guestContents.setWindowOpenHandler(({ url }) => {
|
|
1011
1236
|
const resolved = resolveBrowserNavigationUrl(url);
|
|
1012
1237
|
if (resolved.url) {
|
|
@@ -1023,9 +1248,11 @@ function installBrowserWebviewSecurity({
|
|
|
1023
1248
|
};
|
|
1024
1249
|
}
|
|
1025
1250
|
export {
|
|
1251
|
+
applyBrowserGuestUserAgent,
|
|
1026
1252
|
enforceBrowserWebviewSecurity,
|
|
1027
1253
|
installBrowserWebviewSecurity,
|
|
1028
1254
|
isBrowserNodeWebviewAttach,
|
|
1029
|
-
registerBrowserNodeElectronMain
|
|
1255
|
+
registerBrowserNodeElectronMain,
|
|
1256
|
+
sanitizeBrowserGuestUserAgent
|
|
1030
1257
|
};
|
|
1031
1258
|
//# sourceMappingURL=index.js.map
|