@gjsify/iframe 0.3.21 → 0.4.3

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.
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
@@ -1 +1 @@
1
- import{iframeWidget as e,loaded as t,windowProxy as n}from"./property-symbol.js";import{HTMLElement as r,PropertySymbol as i}from"@gjsify/dom-elements";import{Event as a}from"@gjsify/dom-events";let o,s,c;const{tagName:l,localName:u,namespaceURI:d}=i;var HTMLIFrameElement=class extends r{constructor(){super(),this[o]=null,this[s]=null,this[c]=!1,this[l]=`IFRAME`,this[u]=`iframe`,this[d]=`http://www.w3.org/1999/xhtml`}get src(){return this.getAttribute(`src`)??``}set src(t){let n=this.getAttribute(`src`);this.setAttribute(`src`,t),t!==n&&t&&this[e]&&this[e].loadUri(t)}get srcdoc(){return this.getAttribute(`srcdoc`)??``}set srcdoc(t){let n=this.getAttribute(`srcdoc`);this.setAttribute(`srcdoc`,t),t!==n&&t&&this[e]&&this[e].loadHtml(t)}get name(){return this.getAttribute(`name`)??``}set name(e){this.setAttribute(`name`,e)}get sandbox(){return this.getAttribute(`sandbox`)??``}set sandbox(e){this.setAttribute(`sandbox`,e)}get allow(){return this.getAttribute(`allow`)??``}set allow(e){this.setAttribute(`allow`,e)}get referrerPolicy(){return this.getAttribute(`referrerpolicy`)??``}set referrerPolicy(e){this.setAttribute(`referrerpolicy`,e)}get loading(){let e=this.getAttribute(`loading`);return e===`lazy`||e===`eager`?e:`eager`}set loading(e){this.setAttribute(`loading`,e)}get width(){return this.getAttribute(`width`)??``}set width(e){this.setAttribute(`width`,e)}get height(){return this.getAttribute(`height`)??``}set height(e){this.setAttribute(`height`,e)}get contentWindow(){return this[n]}get contentDocument(){return null}getSVGDocument(){return null}cloneNode(r=!1){let i=super.cloneNode(r);return i[e]=null,i[n]=null,i[t]=!1,i}get[(o=e,s=n,c=t,Symbol.toStringTag)](){return`HTMLIFrameElement`}_onLoad(){this[t]=!0,this.dispatchEvent(new a(`load`))}_onError(){this.dispatchEvent(new a(`error`))}};export{HTMLIFrameElement};
1
+ import"./_virtual/_rolldown/runtime.js";import{iframeWidget as e,loaded as t,windowProxy as n}from"./property-symbol.js";import{HTMLElement as r,PropertySymbol as i}from"@gjsify/dom-elements";import{Event as a}from"@gjsify/dom-events";let o,s,c;const{tagName:l,localName:u,namespaceURI:d}=i;var HTMLIFrameElement=class extends r{constructor(){super(),this[o]=null,this[s]=null,this[c]=!1,this[l]=`IFRAME`,this[u]=`iframe`,this[d]=`http://www.w3.org/1999/xhtml`}get src(){return this.getAttribute(`src`)??``}set src(t){let n=this.getAttribute(`src`);this.setAttribute(`src`,t),t!==n&&t&&this[e]&&this[e].loadUri(t)}get srcdoc(){return this.getAttribute(`srcdoc`)??``}set srcdoc(t){let n=this.getAttribute(`srcdoc`);this.setAttribute(`srcdoc`,t),t!==n&&t&&this[e]&&this[e].loadHtml(t)}get name(){return this.getAttribute(`name`)??``}set name(e){this.setAttribute(`name`,e)}get sandbox(){return this.getAttribute(`sandbox`)??``}set sandbox(e){this.setAttribute(`sandbox`,e)}get allow(){return this.getAttribute(`allow`)??``}set allow(e){this.setAttribute(`allow`,e)}get referrerPolicy(){return this.getAttribute(`referrerpolicy`)??``}set referrerPolicy(e){this.setAttribute(`referrerpolicy`,e)}get loading(){let e=this.getAttribute(`loading`);return e===`lazy`||e===`eager`?e:`eager`}set loading(e){this.setAttribute(`loading`,e)}get width(){return this.getAttribute(`width`)??``}set width(e){this.setAttribute(`width`,e)}get height(){return this.getAttribute(`height`)??``}set height(e){this.setAttribute(`height`,e)}get contentWindow(){return this[n]}get contentDocument(){return null}getSVGDocument(){return null}cloneNode(r=!1){let i=super.cloneNode(r);return i[e]=null,i[n]=null,i[t]=!1,i}get[(o=e,s=n,c=t,Symbol.toStringTag)](){return`HTMLIFrameElement`}_onLoad(){this[t]=!0,this.dispatchEvent(new a(`load`))}_onError(){this.dispatchEvent(new a(`error`))}};export{HTMLIFrameElement};
@@ -1 +1 @@
1
- import{iframeWidget as e,windowProxy as t}from"./property-symbol.js";import{HTMLIFrameElement as n}from"./html-iframe-element.js";import{IFrameWindowProxy as r}from"./iframe-window-proxy.js";import{MessageBridge as i}from"./message-bridge.js";import a from"gi://GObject";import o from"gi://WebKit?version=6.0";const s=a.registerClass({GTypeName:`GjsifyIFrameBridge`},class extends o.WebView{constructor(a){let{enableDeveloperExtras:s,enableJavascript:c,...l}=a??{},u=new o.UserContentManager,d=new o.Settings;d.enable_javascript=c??!0,d.enable_developer_extras=s??!0,super({...l,user_content_manager:u,settings:d}),this._readyCallbacks=[],this._options={enableDeveloperExtras:s,enableJavascript:c},this._iframe=new n,this._iframe[e]=this,this._messageBridge=new i(this);let f=new r(this._messageBridge);this._iframe[t]=f,this._messageBridge.setWindowProxy(f),this.connect(`load-changed`,(e,t)=>{switch(t){case o.LoadEvent.COMMITTED:{let e=this.get_uri();e&&this._messageBridge.updateUri(e);break}case o.LoadEvent.FINISHED:this._iframe._onLoad();for(let e of this._readyCallbacks)e(this._iframe);this._readyCallbacks=[];break}}),this.connect(`load-failed`,()=>(this._iframe._onError(),!1)),this.connect(`unrealize`,()=>{this._messageBridge.destroy();let n=this._iframe[t];n&&n._close(),this._iframe[e]=null,this._iframe[t]=null})}get iframeElement(){return this._iframe}onReady(e){this._readyCallbacks.push(e)}loadUri(e){this._iframe.setAttribute(`src`,e),this.load_uri(e)}loadHtml(e,t){this._iframe.setAttribute(`srcdoc`,e),this.load_html(e,t??`about:srcdoc`)}postMessage(e,t=`*`){this._messageBridge.sendToWebView(e,t)}installGlobals(){Object.defineProperty(globalThis,`HTMLIFrameElement`,{value:n,writable:!0,configurable:!0})}});export{s as IFrameBridge};
1
+ import"./_virtual/_rolldown/runtime.js";import{iframeWidget as e,windowProxy as t}from"./property-symbol.js";import{HTMLIFrameElement as n}from"./html-iframe-element.js";import{IFrameWindowProxy as r}from"./iframe-window-proxy.js";import{MessageBridge as i}from"./message-bridge.js";import a from"gi://GObject";import o from"gi://WebKit?version=6.0";const s=a.registerClass({GTypeName:`GjsifyIFrameBridge`},class IFrameBridge extends o.WebView{constructor(a){let{enableDeveloperExtras:s,enableJavascript:c,...l}=a??{},u=new o.UserContentManager,d=new o.Settings;d.enable_javascript=c??!0,d.enable_developer_extras=s??!0,super({...l,user_content_manager:u,settings:d}),this._readyCallbacks=[],this._options={enableDeveloperExtras:s,enableJavascript:c},this._iframe=new n,this._iframe[e]=this,this._messageBridge=new i(this);let f=new r(this._messageBridge);this._iframe[t]=f,this._messageBridge.setWindowProxy(f),this.connect(`load-changed`,(e,t)=>{switch(t){case o.LoadEvent.COMMITTED:{let e=this.get_uri();e&&this._messageBridge.updateUri(e);break}case o.LoadEvent.FINISHED:this._iframe._onLoad();for(let e of this._readyCallbacks)e(this._iframe);this._readyCallbacks=[];break}}),this.connect(`load-failed`,()=>(this._iframe._onError(),!1)),this.connect(`unrealize`,()=>{this._messageBridge.destroy();let n=this._iframe[t];n&&n._close(),this._iframe[e]=null,this._iframe[t]=null})}get iframeElement(){return this._iframe}onReady(e){this._readyCallbacks.push(e)}loadUri(e){this._iframe.setAttribute(`src`,e),this.load_uri(e)}loadHtml(e,t){this._iframe.setAttribute(`srcdoc`,e),this.load_html(e,t??`about:srcdoc`)}postMessage(e,t=`*`){this._messageBridge.sendToWebView(e,t)}installGlobals(){Object.defineProperty(globalThis,`HTMLIFrameElement`,{value:n,writable:!0,configurable:!0})}});export{s as IFrameBridge};
@@ -1 +1 @@
1
- import{EventTarget as e}from"@gjsify/dom-events";var IFrameWindowProxy=class extends e{constructor(e){super(),this._closed=!1,this._bridge=e}postMessage(e,t=`*`){this._closed||this._bridge.sendToWebView(e,t)}get location(){return this._bridge.getLocation()}get parent(){return globalThis}get top(){return globalThis}get self(){return this}get window(){return this}get closed(){return this._closed}_close(){this._closed=!0}get[Symbol.toStringTag](){return`IFrameWindowProxy`}};export{IFrameWindowProxy};
1
+ import"./_virtual/_rolldown/runtime.js";import{EventTarget as e}from"@gjsify/dom-events";var IFrameWindowProxy=class extends e{constructor(e){super(),this._closed=!1,this._bridge=e}postMessage(e,t=`*`){this._closed||this._bridge.sendToWebView(e,t)}get location(){return this._bridge.getLocation()}get parent(){return globalThis}get top(){return globalThis}get self(){return this}get window(){return this}get closed(){return this._closed}_close(){this._closed=!0}get[Symbol.toStringTag](){return`IFrameWindowProxy`}};export{IFrameWindowProxy};
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{HTMLIFrameElement as e}from"./html-iframe-element.js";import{IFrameWindowProxy as t}from"./iframe-window-proxy.js";import{MessageBridge as n}from"./message-bridge.js";import{IFrameBridge as r}from"./iframe-bridge.js";import{Document as i}from"@gjsify/dom-elements";i.registerElementFactory(`iframe`,()=>new e),Object.defineProperty(globalThis,`HTMLIFrameElement`,{value:e,writable:!0,configurable:!0});export{e as HTMLIFrameElement,r as IFrameBridge,t as IFrameWindowProxy,n as MessageBridge};
1
+ import"./_virtual/_rolldown/runtime.js";import{HTMLIFrameElement as e}from"./html-iframe-element.js";import{IFrameWindowProxy as t}from"./iframe-window-proxy.js";import{MessageBridge as n}from"./message-bridge.js";import{IFrameBridge as r}from"./iframe-bridge.js";import{Document as i}from"@gjsify/dom-elements";i.registerElementFactory(`iframe`,()=>new e),Object.defineProperty(globalThis,`HTMLIFrameElement`,{value:e,writable:!0,configurable:!0});export{e as HTMLIFrameElement,r as IFrameBridge,t as IFrameWindowProxy,n as MessageBridge};
@@ -1,4 +1,4 @@
1
- import{MessageEvent as e}from"@gjsify/dom-events";import t from"gi://WebKit?version=6.0";import n from"gi://Gio?version=2.0";n._promisify(t.WebView.prototype,`evaluate_javascript`,`evaluate_javascript_finish`);const r=`gjsify-iframe`,i=`(function() {
1
+ import"./_virtual/_rolldown/runtime.js";import{MessageEvent as e}from"@gjsify/dom-events";import t from"gi://WebKit?version=6.0";import n from"gi://Gio?version=2.0";n._promisify(t.WebView.prototype,`evaluate_javascript`,`evaluate_javascript_finish`);const r=`gjsify-iframe`,i=`(function() {
2
2
  var handler = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers['${r}'];
3
3
  if (!handler) return;
4
4
  function bridgePostMessage(data, targetOrigin) {
package/package.json CHANGED
@@ -1,46 +1,49 @@
1
1
  {
2
- "name": "@gjsify/iframe",
3
- "version": "0.3.21",
4
- "description": "HTMLIFrameElement for GJS, backed by WebKit.WebView",
5
- "type": "module",
6
- "module": "lib/esm/index.js",
7
- "types": "lib/types/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/types/index.d.ts",
11
- "default": "./lib/esm/index.js"
2
+ "name": "@gjsify/iframe",
3
+ "version": "0.4.3",
4
+ "description": "HTMLIFrameElement for GJS, backed by WebKit.WebView",
5
+ "type": "module",
6
+ "module": "lib/esm/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/esm/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "lib"
16
+ ],
17
+ "scripts": {
18
+ "clear": "rm -rf lib tmp tsconfig.tsbuildinfo test.gjs.mjs || exit 0",
19
+ "check": "tsc --noEmit",
20
+ "build": "gjsify run build:gjsify && gjsify run build:types",
21
+ "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
22
+ "build:types": "tsc",
23
+ "build:test": "gjsify run build:test:gjs",
24
+ "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
25
+ "test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:gjs",
26
+ "test:gjs": "gjsify run test.gjs.mjs"
27
+ },
28
+ "keywords": [
29
+ "gjs",
30
+ "iframe",
31
+ "webview",
32
+ "webkit",
33
+ "dom"
34
+ ],
35
+ "dependencies": {
36
+ "@girs/gjs": "4.0.0-rc.15",
37
+ "@girs/gtk-4.0": "4.23.0-4.0.0-rc.15",
38
+ "@girs/javascriptcore-6.0": "2.52.1-4.0.0-rc.15",
39
+ "@girs/webkit-6.0": "2.52.1-4.0.0-rc.15",
40
+ "@gjsify/dom-elements": "workspace:^",
41
+ "@gjsify/dom-events": "workspace:^"
42
+ },
43
+ "devDependencies": {
44
+ "@gjsify/cli": "workspace:^",
45
+ "@gjsify/unit": "workspace:^",
46
+ "@types/node": "^25.6.2",
47
+ "typescript": "^6.0.3"
12
48
  }
13
- },
14
- "scripts": {
15
- "clear": "rm -rf lib tmp tsconfig.tsbuildinfo test.gjs.mjs || exit 0",
16
- "check": "tsc --noEmit",
17
- "build": "yarn build:gjsify && yarn build:types",
18
- "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
19
- "build:types": "tsc",
20
- "build:test": "yarn build:test:gjs",
21
- "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
22
- "test": "yarn build:gjsify && yarn build:test && yarn test:gjs",
23
- "test:gjs": "gjsify run test.gjs.mjs"
24
- },
25
- "keywords": [
26
- "gjs",
27
- "iframe",
28
- "webview",
29
- "webkit",
30
- "dom"
31
- ],
32
- "dependencies": {
33
- "@girs/gjs": "4.0.0-rc.14",
34
- "@girs/gtk-4.0": "4.23.0-4.0.0-rc.14",
35
- "@girs/javascriptcore-6.0": "2.52.1-4.0.0-rc.14",
36
- "@girs/webkit-6.0": "2.52.1-4.0.0-rc.14",
37
- "@gjsify/dom-elements": "^0.3.21",
38
- "@gjsify/dom-events": "^0.3.21"
39
- },
40
- "devDependencies": {
41
- "@gjsify/cli": "^0.3.21",
42
- "@gjsify/unit": "^0.3.21",
43
- "@types/node": "^25.6.2",
44
- "typescript": "^6.0.3"
45
- }
46
- }
49
+ }
@@ -1,177 +0,0 @@
1
- // HTMLIFrameElement for GJS — original implementation using WebKit.WebView
2
- // Reference: refs/happy-dom/packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElement.ts
3
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement
4
-
5
- import { HTMLElement, PropertySymbol } from '@gjsify/dom-elements';
6
- import { Event } from '@gjsify/dom-events';
7
- import * as PS from './property-symbol.js';
8
-
9
- import type { IFrameWindowProxy } from './iframe-window-proxy.js';
10
-
11
- const { tagName, localName, namespaceURI } = PropertySymbol;
12
-
13
- /**
14
- * HTML IFrame Element.
15
- *
16
- * Backed by WebKit.WebView when connected to an IFrameBridge.
17
- * Without a backing widget, behaves as a pure DOM element with attribute storage.
18
- *
19
- * Reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement
20
- */
21
- export class HTMLIFrameElement extends HTMLElement {
22
- /** @internal The backing IFrameBridge (set by IFrameBridge when it creates this element) */
23
- [PS.iframeWidget]: import('./iframe-bridge.js').IFrameBridge | null = null;
24
-
25
- /** @internal The contentWindow proxy */
26
- [PS.windowProxy]: IFrameWindowProxy | null = null;
27
-
28
- /** @internal Whether content has been loaded */
29
- [PS.loaded]: boolean = false;
30
-
31
- constructor() {
32
- super();
33
- this[tagName] = 'IFRAME';
34
- this[localName] = 'iframe';
35
- this[namespaceURI] = 'http://www.w3.org/1999/xhtml';
36
- }
37
-
38
- // -- Attribute-backed string properties --
39
-
40
- get src(): string {
41
- return this.getAttribute('src') ?? '';
42
- }
43
-
44
- set src(value: string) {
45
- const old = this.getAttribute('src');
46
- this.setAttribute('src', value);
47
- if (value !== old && value && this[PS.iframeWidget]) {
48
- this[PS.iframeWidget].loadUri(value);
49
- }
50
- }
51
-
52
- get srcdoc(): string {
53
- return this.getAttribute('srcdoc') ?? '';
54
- }
55
-
56
- set srcdoc(value: string) {
57
- const old = this.getAttribute('srcdoc');
58
- this.setAttribute('srcdoc', value);
59
- if (value !== old && value && this[PS.iframeWidget]) {
60
- this[PS.iframeWidget].loadHtml(value);
61
- }
62
- }
63
-
64
- get name(): string {
65
- return this.getAttribute('name') ?? '';
66
- }
67
-
68
- set name(value: string) {
69
- this.setAttribute('name', value);
70
- }
71
-
72
- get sandbox(): string {
73
- return this.getAttribute('sandbox') ?? '';
74
- }
75
-
76
- set sandbox(value: string) {
77
- this.setAttribute('sandbox', value);
78
- }
79
-
80
- get allow(): string {
81
- return this.getAttribute('allow') ?? '';
82
- }
83
-
84
- set allow(value: string) {
85
- this.setAttribute('allow', value);
86
- }
87
-
88
- get referrerPolicy(): string {
89
- return this.getAttribute('referrerpolicy') ?? '';
90
- }
91
-
92
- set referrerPolicy(value: string) {
93
- this.setAttribute('referrerpolicy', value);
94
- }
95
-
96
- get loading(): string {
97
- const value = this.getAttribute('loading');
98
- if (value === 'lazy' || value === 'eager') return value;
99
- return 'eager';
100
- }
101
-
102
- set loading(value: string) {
103
- this.setAttribute('loading', value);
104
- }
105
-
106
- // -- Attribute-backed string properties (width/height are strings per spec) --
107
-
108
- get width(): string {
109
- return this.getAttribute('width') ?? '';
110
- }
111
-
112
- set width(value: string) {
113
- this.setAttribute('width', value);
114
- }
115
-
116
- get height(): string {
117
- return this.getAttribute('height') ?? '';
118
- }
119
-
120
- set height(value: string) {
121
- this.setAttribute('height', value);
122
- }
123
-
124
- // -- Content access --
125
-
126
- /**
127
- * Returns the window proxy for the iframe's content.
128
- * Available after the IFrameBridge has loaded content.
129
- */
130
- get contentWindow(): IFrameWindowProxy | null {
131
- return this[PS.windowProxy];
132
- }
133
-
134
- /**
135
- * Always returns null — cross-context boundary.
136
- * The WebView content runs in a separate process; direct document access
137
- * is not feasible. Use postMessage() for communication.
138
- */
139
- get contentDocument(): null {
140
- return null;
141
- }
142
-
143
- // -- Methods --
144
-
145
- /**
146
- * Returns a promise that resolves to the iframe's src URL.
147
- */
148
- getSVGDocument(): null {
149
- return null;
150
- }
151
-
152
- cloneNode(deep = false): HTMLIFrameElement {
153
- const clone = super.cloneNode(deep) as HTMLIFrameElement;
154
- // Cloned iframes are not connected to any widget
155
- clone[PS.iframeWidget] = null;
156
- clone[PS.windowProxy] = null;
157
- clone[PS.loaded] = false;
158
- return clone;
159
- }
160
-
161
- get [Symbol.toStringTag](): string {
162
- return 'HTMLIFrameElement';
163
- }
164
-
165
- // -- Internal: called by IFrameBridge --
166
-
167
- /** @internal Fire load event */
168
- _onLoad(): void {
169
- this[PS.loaded] = true;
170
- this.dispatchEvent(new Event('load'));
171
- }
172
-
173
- /** @internal Fire error event */
174
- _onError(): void {
175
- this.dispatchEvent(new Event('error'));
176
- }
177
- }
@@ -1,157 +0,0 @@
1
- // IFrameBridge — GTK container for HTMLIFrameElement backed by WebKit.WebView.
2
- // Provides a WebKit.WebView subclass that bundles all iframe bootstrapping.
3
-
4
- import GObject from 'gi://GObject';
5
- import WebKit from 'gi://WebKit?version=6.0';
6
-
7
- import { HTMLIFrameElement } from './html-iframe-element.js';
8
- import { IFrameWindowProxy } from './iframe-window-proxy.js';
9
- import { MessageBridge } from './message-bridge.js';
10
- import * as PS from './property-symbol.js';
11
-
12
- import type { IFrameBridgeOptions, IFrameReadyCallback } from './types/index.js';
13
-
14
- /**
15
- * A `WebKit.WebView` subclass that handles iframe bootstrapping:
16
- * - Sets up WebKit settings (JavaScript, developer extras)
17
- * - Creates an `HTMLIFrameElement` wrapping this WebView
18
- * - Sets up postMessage bridge for GJS ↔ WebView communication
19
- * - Fires `onReady()` callbacks with the iframe element once loaded
20
- * - `installGlobals()` sets `globalThis.HTMLIFrameElement`
21
- *
22
- * Usage:
23
- * ```ts
24
- * const iframeWidget = new IFrameBridge();
25
- * iframeWidget.installGlobals();
26
- * iframeWidget.onReady((iframe) => {
27
- * iframe.contentWindow?.addEventListener('message', (e) => {
28
- * console.log('Message from iframe:', e.data);
29
- * });
30
- * });
31
- * iframeWidget.iframeElement.src = 'https://example.com';
32
- * window.set_child(iframeWidget);
33
- * ```
34
- */
35
- export const IFrameBridge = GObject.registerClass(
36
- { GTypeName: 'GjsifyIFrameBridge' },
37
- class IFrameBridge extends WebKit.WebView {
38
- _iframe: HTMLIFrameElement;
39
- _messageBridge: MessageBridge;
40
- _readyCallbacks: IFrameReadyCallback[] = [];
41
- _options: IFrameBridgeOptions;
42
-
43
- constructor(options?: IFrameBridgeOptions & Partial<WebKit.WebView.ConstructorProps>) {
44
- const { enableDeveloperExtras, enableJavascript, ...webViewProps } = options ?? {};
45
-
46
- const userContentManager = new WebKit.UserContentManager();
47
- const settings = new WebKit.Settings();
48
- settings.enable_javascript = enableJavascript ?? true;
49
- settings.enable_developer_extras = enableDeveloperExtras ?? true;
50
-
51
- super({
52
- ...webViewProps,
53
- user_content_manager: userContentManager,
54
- settings,
55
- });
56
-
57
- this._options = { enableDeveloperExtras, enableJavascript };
58
-
59
- // Create the DOM element and link it to this widget
60
- this._iframe = new HTMLIFrameElement();
61
- this._iframe[PS.iframeWidget] = this as unknown as import('./iframe-bridge.js').IFrameBridge;
62
-
63
- // Set up the message bridge
64
- this._messageBridge = new MessageBridge(this);
65
-
66
- // Create the window proxy and connect it
67
- const windowProxy = new IFrameWindowProxy(this._messageBridge);
68
- this._iframe[PS.windowProxy] = windowProxy;
69
- this._messageBridge.setWindowProxy(windowProxy);
70
-
71
- // Track load state
72
- this.connect('load-changed', (_webView: WebKit.WebView, event: WebKit.LoadEvent) => {
73
- switch (event) {
74
- case WebKit.LoadEvent.COMMITTED: {
75
- const uri = this.get_uri();
76
- if (uri) this._messageBridge.updateUri(uri);
77
- break;
78
- }
79
- case WebKit.LoadEvent.FINISHED:
80
- this._iframe._onLoad();
81
- for (const cb of this._readyCallbacks) {
82
- cb(this._iframe as unknown as globalThis.HTMLIFrameElement);
83
- }
84
- this._readyCallbacks = [];
85
- break;
86
- }
87
- });
88
-
89
- this.connect('load-failed', () => {
90
- this._iframe._onError();
91
- return false;
92
- });
93
-
94
- this.connect('unrealize', () => {
95
- this._messageBridge.destroy();
96
- const proxy = this._iframe[PS.windowProxy];
97
- if (proxy) {
98
- proxy._close();
99
- }
100
- this._iframe[PS.iframeWidget] = null;
101
- this._iframe[PS.windowProxy] = null;
102
- });
103
- }
104
-
105
- /** The HTMLIFrameElement wrapping this WebView. */
106
- get iframeElement(): HTMLIFrameElement {
107
- return this._iframe;
108
- }
109
-
110
- /**
111
- * Register a callback to be invoked when content has loaded.
112
- * If content is already loaded, the callback fires on next load.
113
- */
114
- onReady(cb: IFrameReadyCallback): void {
115
- this._readyCallbacks.push(cb);
116
- }
117
-
118
- /**
119
- * Load a URI into the WebView.
120
- * Also updates the iframe element's src attribute.
121
- */
122
- loadUri(uri: string): void {
123
- this._iframe.setAttribute('src', uri);
124
- this.load_uri(uri);
125
- }
126
-
127
- /**
128
- * Load inline HTML into the WebView.
129
- * Also updates the iframe element's srcdoc attribute.
130
- */
131
- loadHtml(html: string, baseUri?: string): void {
132
- this._iframe.setAttribute('srcdoc', html);
133
- this.load_html(html, baseUri ?? 'about:srcdoc');
134
- }
135
-
136
- /**
137
- * Send a message to the WebView content via the standard postMessage API.
138
- * Equivalent to `this.iframeElement.contentWindow.postMessage(message, targetOrigin)`.
139
- */
140
- postMessage(message: unknown, targetOrigin = '*'): void {
141
- this._messageBridge.sendToWebView(message, targetOrigin);
142
- }
143
-
144
- /**
145
- * Set `globalThis.HTMLIFrameElement` to the gjsify implementation.
146
- */
147
- installGlobals(): void {
148
- Object.defineProperty(globalThis, 'HTMLIFrameElement', {
149
- value: HTMLIFrameElement,
150
- writable: true,
151
- configurable: true,
152
- });
153
- }
154
- },
155
- );
156
-
157
- export type IFrameBridge = InstanceType<typeof IFrameBridge>;
@@ -1,86 +0,0 @@
1
- // IFrameWindowProxy for GJS — lightweight Window proxy for iframe contentWindow
2
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
3
- // Reference: refs/happy-dom/packages/happy-dom/src/window/CrossOriginBrowserWindow.ts
4
-
5
- import { EventTarget } from '@gjsify/dom-events';
6
-
7
- import type { MessageBridge } from './message-bridge.js';
8
-
9
- /**
10
- * Lightweight Window-like proxy returned by `HTMLIFrameElement.contentWindow`.
11
- *
12
- * Supports the subset of the Window API needed for cross-origin iframe communication:
13
- * - `postMessage()` for sending messages to the iframe content
14
- * - `addEventListener('message', ...)` for receiving messages from the iframe content
15
- * - `location` (read-only) reflecting the current URI
16
- * - `parent` reference to the host window
17
- * - `closed` status
18
- *
19
- * This is intentionally NOT a full BrowserWindow — just enough for standard
20
- * postMessage-based communication patterns.
21
- */
22
- export class IFrameWindowProxy extends EventTarget {
23
- private _bridge: MessageBridge;
24
- private _closed = false;
25
-
26
- constructor(bridge: MessageBridge) {
27
- super();
28
- this._bridge = bridge;
29
- }
30
-
31
- /**
32
- * Send a message to the iframe content.
33
- *
34
- * @param message - Data to send (must be JSON-serializable)
35
- * @param targetOrigin - Target origin for the message. Default: '*'
36
- */
37
- postMessage(message: unknown, targetOrigin = '*'): void {
38
- if (this._closed) return;
39
- this._bridge.sendToWebView(message, targetOrigin);
40
- }
41
-
42
- /**
43
- * Read-only location reflecting the current WebView URI.
44
- */
45
- get location(): { href: string; origin: string } {
46
- return this._bridge.getLocation();
47
- }
48
-
49
- /**
50
- * Reference to the host (parent) window — in GJS this is globalThis.
51
- */
52
- get parent(): typeof globalThis {
53
- return globalThis;
54
- }
55
-
56
- /**
57
- * Reference to the top-level window — in GJS this is globalThis.
58
- */
59
- get top(): typeof globalThis {
60
- return globalThis;
61
- }
62
-
63
- /**
64
- * The window itself (self-reference per spec).
65
- */
66
- get self(): IFrameWindowProxy {
67
- return this;
68
- }
69
-
70
- get window(): IFrameWindowProxy {
71
- return this;
72
- }
73
-
74
- get closed(): boolean {
75
- return this._closed;
76
- }
77
-
78
- /** @internal Mark as closed when the WebView is destroyed */
79
- _close(): void {
80
- this._closed = true;
81
- }
82
-
83
- get [Symbol.toStringTag](): string {
84
- return 'IFrameWindowProxy';
85
- }
86
- }