@openfin/web-utils 0.45.69 → 0.45.70
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/out/custom-elements/of-view.d.ts +22 -0
- package/out/custom-elements/of-view.js +68 -1
- package/out/utils/utils.d.ts +10 -0
- package/out/utils/utils.js +29 -1
- package/package.json +1 -1
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
declare const mixin: (Base: typeof HTMLElement) => {
|
|
2
2
|
new (): {
|
|
3
3
|
"__#1@#titleObserver": MutationObserver | null;
|
|
4
|
+
"__#1@#faviconObserver": MutationObserver | null;
|
|
5
|
+
"__#1@#isLoaded": boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The last favicon URL dispatched by this element, regardless of which path
|
|
8
|
+
* discovered it (same-origin DOM or data channel).
|
|
9
|
+
* Set via `#emitFaviconEvent` for element-driven paths, or written directly
|
|
10
|
+
* by `LayoutView.setFavicon` for the data-channel path.
|
|
11
|
+
*/
|
|
12
|
+
favicon: string | null;
|
|
13
|
+
/** Centralised favicon dispatch: caches the URL and fires the DOM event. */
|
|
14
|
+
"__#1@#emitFaviconEvent"(favicon: string): void;
|
|
4
15
|
"__#1@#initObservers"(): void;
|
|
5
16
|
"__#1@#clearObservers"(): void;
|
|
6
17
|
readonly lastKnownUrl: string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the favicon URL for this view's same-origin iframe, or `null` if the
|
|
20
|
+
* frame is cross-origin, not yet loaded, or has no favicon link element.
|
|
21
|
+
*
|
|
22
|
+
* For same-origin frames the value is read synchronously from the DOM and the
|
|
23
|
+
* favicon MutationObserver is (re-)initialised so future changes are tracked.
|
|
24
|
+
* Cross-origin frames without the OpenFin connect script return `null` — favicon
|
|
25
|
+
* updates for those frames are delivered exclusively via the data channel.
|
|
26
|
+
*/
|
|
27
|
+
getOrDiscoverFavicon(): Promise<string | null>;
|
|
7
28
|
connectedCallback(): void;
|
|
8
29
|
readonly iframe: HTMLIFrameElement | null;
|
|
9
30
|
title: string;
|
|
@@ -15,6 +36,7 @@ declare const mixin: (Base: typeof HTMLElement) => {
|
|
|
15
36
|
providerId: string | null;
|
|
16
37
|
contextGroup: string | null;
|
|
17
38
|
allow: string | null;
|
|
39
|
+
showFavicon: boolean;
|
|
18
40
|
accessKey: string;
|
|
19
41
|
readonly accessKeyLabel: string;
|
|
20
42
|
autocapitalize: string;
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { encodeOptions } from '../utils/options-encoder';
|
|
2
|
-
import { getTitleObserver, isSameOrigin } from '../utils/utils';
|
|
2
|
+
import { getFaviconObserver, getTitleObserver, isSameOrigin } from '../utils/utils';
|
|
3
3
|
const mixin = (Base) => class _OfViewElement extends Base {
|
|
4
4
|
constructor() {
|
|
5
5
|
super(...arguments);
|
|
6
6
|
this.#titleObserver = null;
|
|
7
|
+
this.#faviconObserver = null;
|
|
8
|
+
this.#isLoaded = false;
|
|
9
|
+
/**
|
|
10
|
+
* The last favicon URL dispatched by this element, regardless of which path
|
|
11
|
+
* discovered it (same-origin DOM or data channel).
|
|
12
|
+
* Set via `#emitFaviconEvent` for element-driven paths, or written directly
|
|
13
|
+
* by `LayoutView.setFavicon` for the data-channel path.
|
|
14
|
+
*/
|
|
15
|
+
this.favicon = null;
|
|
7
16
|
}
|
|
8
17
|
#titleObserver;
|
|
18
|
+
#faviconObserver;
|
|
19
|
+
#isLoaded;
|
|
20
|
+
/** Centralised favicon dispatch: caches the URL and fires the DOM event. */
|
|
21
|
+
#emitFaviconEvent(favicon) {
|
|
22
|
+
this.favicon = favicon;
|
|
23
|
+
this.dispatchEvent(new CustomEvent('page-favicon-updated', { detail: { favicon } }));
|
|
24
|
+
}
|
|
9
25
|
#initObservers() {
|
|
10
26
|
// we can only monitor the DOM if the iframe is same-origin
|
|
11
27
|
const iframe = this.iframe;
|
|
@@ -17,6 +33,13 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
17
33
|
this.#titleObserver = getTitleObserver(iframe.contentDocument.head, (title) => this.dispatchEvent(new CustomEvent('page-title-updated', { detail: { title } })));
|
|
18
34
|
// Emit initial document.title
|
|
19
35
|
this.dispatchEvent(new CustomEvent('page-title-updated', { detail: { title: iframe.contentDocument.title } }));
|
|
36
|
+
if (this.showFavicon) {
|
|
37
|
+
this.#faviconObserver = getFaviconObserver(iframe.contentDocument.head, (favicon) => this.#emitFaviconEvent(favicon));
|
|
38
|
+
const initialFavicon = iframe.contentDocument.querySelector('link[rel~="icon"]')?.href;
|
|
39
|
+
if (initialFavicon) {
|
|
40
|
+
this.#emitFaviconEvent(initialFavicon);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
20
43
|
}
|
|
21
44
|
}
|
|
22
45
|
#clearObservers() {
|
|
@@ -24,10 +47,42 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
24
47
|
this.#titleObserver.disconnect();
|
|
25
48
|
this.#titleObserver = null;
|
|
26
49
|
}
|
|
50
|
+
if (this.#faviconObserver) {
|
|
51
|
+
this.#faviconObserver.disconnect();
|
|
52
|
+
this.#faviconObserver = null;
|
|
53
|
+
}
|
|
27
54
|
}
|
|
28
55
|
get lastKnownUrl() {
|
|
29
56
|
return this.iframe?.contentDocument?.location.href;
|
|
30
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the favicon URL for this view's same-origin iframe, or `null` if the
|
|
60
|
+
* frame is cross-origin, not yet loaded, or has no favicon link element.
|
|
61
|
+
*
|
|
62
|
+
* For same-origin frames the value is read synchronously from the DOM and the
|
|
63
|
+
* favicon MutationObserver is (re-)initialised so future changes are tracked.
|
|
64
|
+
* Cross-origin frames without the OpenFin connect script return `null` — favicon
|
|
65
|
+
* updates for those frames are delivered exclusively via the data channel.
|
|
66
|
+
*/
|
|
67
|
+
getOrDiscoverFavicon() {
|
|
68
|
+
if (!this.#isLoaded)
|
|
69
|
+
return Promise.resolve(null);
|
|
70
|
+
const iframe = this.iframe;
|
|
71
|
+
if (!iframe)
|
|
72
|
+
return Promise.resolve(null);
|
|
73
|
+
const isSame = !!(iframe.contentDocument?.head &&
|
|
74
|
+
iframe.contentWindow &&
|
|
75
|
+
window.top &&
|
|
76
|
+
isSameOrigin(iframe.contentWindow, window.top));
|
|
77
|
+
if (isSame && iframe.contentDocument) {
|
|
78
|
+
// Ensure the favicon observer is running for future DOM changes.
|
|
79
|
+
if (!this.#faviconObserver) {
|
|
80
|
+
this.#faviconObserver = getFaviconObserver(iframe.contentDocument.head, (favicon) => this.#emitFaviconEvent(favicon));
|
|
81
|
+
}
|
|
82
|
+
return Promise.resolve(iframe.contentDocument.querySelector('link[rel~="icon"]')?.href ?? null);
|
|
83
|
+
}
|
|
84
|
+
return Promise.resolve(null);
|
|
85
|
+
}
|
|
31
86
|
connectedCallback() {
|
|
32
87
|
if (!this.name || !this.uuid) {
|
|
33
88
|
throw new Error('<of-view> Name or uuid attribute missing');
|
|
@@ -38,6 +93,7 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
38
93
|
if (!this.iframe) {
|
|
39
94
|
const iframe = document.createElement('iframe');
|
|
40
95
|
iframe.addEventListener('load', () => {
|
|
96
|
+
this.#isLoaded = true;
|
|
41
97
|
// reload observers every navigation
|
|
42
98
|
this.#initObservers();
|
|
43
99
|
});
|
|
@@ -145,6 +201,17 @@ const mixin = (Base) => class _OfViewElement extends Base {
|
|
|
145
201
|
this.setAttribute('allow', val);
|
|
146
202
|
}
|
|
147
203
|
}
|
|
204
|
+
get showFavicon() {
|
|
205
|
+
return this.hasAttribute('of-show-favicon');
|
|
206
|
+
}
|
|
207
|
+
set showFavicon(val) {
|
|
208
|
+
if (val) {
|
|
209
|
+
this.setAttribute('of-show-favicon', '');
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
this.removeAttribute('of-show-favicon');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
148
215
|
static get observedAttributes() {
|
|
149
216
|
return ['name'];
|
|
150
217
|
}
|
package/out/utils/utils.d.ts
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
export declare const isSameOrigin: (windowA: Window | WindowProxy, windowB: Window | WindowProxy) => boolean;
|
|
2
2
|
export declare const getTitleObserver: (documentHead: Node, setter: (title: string | null) => void) => MutationObserver;
|
|
3
|
+
/**
|
|
4
|
+
* Observe the canonical `link[rel~="icon"]` element in a document and report its href when it
|
|
5
|
+
* changes. The callback is only invoked when the resolved href differs from the last emitted
|
|
6
|
+
* value, preventing spurious re-emissions on unrelated `<head>` mutations.
|
|
7
|
+
*
|
|
8
|
+
* **Cross-origin / Core Web connect:** dynamic favicon detection requires the loaded page to be
|
|
9
|
+
* same-origin with the shell, or to use OpenFin Core Web `connect` to establish the data channel.
|
|
10
|
+
* Arbitrary third-party pages without the OpenFin connect script will not update tab icons dynamically.
|
|
11
|
+
*/
|
|
12
|
+
export declare const getFaviconObserver: (documentHead: HTMLHeadElement, callback: (favicon: string) => void) => MutationObserver;
|
package/out/utils/utils.js
CHANGED
|
@@ -47,4 +47,32 @@ export const getTitleObserver = (documentHead, setter) => {
|
|
|
47
47
|
observer.observe(documentHead, { childList: true, subtree: true, characterData: true });
|
|
48
48
|
return observer;
|
|
49
49
|
};
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Observe the canonical `link[rel~="icon"]` element in a document and report its href when it
|
|
52
|
+
* changes. The callback is only invoked when the resolved href differs from the last emitted
|
|
53
|
+
* value, preventing spurious re-emissions on unrelated `<head>` mutations.
|
|
54
|
+
*
|
|
55
|
+
* **Cross-origin / Core Web connect:** dynamic favicon detection requires the loaded page to be
|
|
56
|
+
* same-origin with the shell, or to use OpenFin Core Web `connect` to establish the data channel.
|
|
57
|
+
* Arbitrary third-party pages without the OpenFin connect script will not update tab icons dynamically.
|
|
58
|
+
*/
|
|
59
|
+
export const getFaviconObserver = (documentHead, callback) => {
|
|
60
|
+
let lastHref;
|
|
61
|
+
const run = () => {
|
|
62
|
+
const href = documentHead.querySelector('link[rel~="icon"]')?.href;
|
|
63
|
+
if (href && href !== lastHref) {
|
|
64
|
+
lastHref = href;
|
|
65
|
+
callback(href);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const observer = new MutationObserver(() => {
|
|
69
|
+
run();
|
|
70
|
+
});
|
|
71
|
+
observer.observe(documentHead, {
|
|
72
|
+
childList: true,
|
|
73
|
+
subtree: true,
|
|
74
|
+
attributes: true,
|
|
75
|
+
attributeFilter: ['href', 'rel']
|
|
76
|
+
});
|
|
77
|
+
return observer;
|
|
78
|
+
};
|