@elliemae/ssf-host 2.23.4 → 2.24.0
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/cjs/callchain-host.html +262 -0
- package/dist/cjs/callchain-intermediate.html +92 -0
- package/dist/cjs/e2e-host.html +603 -0
- package/dist/cjs/e2e-index.html +234 -0
- package/dist/cjs/host.js +100 -43
- package/dist/cjs/index.html +304 -151
- package/dist/cjs/popup-focus-host.html +318 -0
- package/dist/cjs/utils.js +14 -1
- package/dist/cjs/v2-host-v1-guest.html +3 -0
- package/dist/esm/callchain-host.html +262 -0
- package/dist/esm/callchain-intermediate.html +92 -0
- package/dist/esm/e2e-host.html +603 -0
- package/dist/esm/e2e-index.html +234 -0
- package/dist/esm/host.js +101 -44
- package/dist/esm/index.html +304 -151
- package/dist/esm/popup-focus-host.html +318 -0
- package/dist/esm/utils.js +14 -1
- package/dist/esm/v2-host-v1-guest.html +3 -0
- package/dist/public/callchain-host.html +34 -0
- package/dist/public/callchain-host.js +3 -0
- package/dist/public/callchain-host.js.br +0 -0
- package/dist/public/callchain-host.js.gz +0 -0
- package/dist/public/callchain-host.js.map +1 -0
- package/dist/public/callchain-intermediate.html +1 -0
- package/dist/public/e2e-host.html +5 -0
- package/dist/public/e2e-host.js +8 -0
- package/dist/public/e2e-host.js.br +0 -0
- package/dist/public/e2e-host.js.gz +0 -0
- package/dist/public/e2e-host.js.map +1 -0
- package/dist/public/e2e-index.html +1 -0
- package/dist/public/index.html +1 -1
- package/dist/public/init.js +1 -1
- package/dist/public/init.js.br +0 -0
- package/dist/public/init.js.gz +0 -0
- package/dist/public/init.js.map +1 -1
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js +3 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.br +0 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.gz +0 -0
- package/dist/public/js/emuiSsfHost.071827d0d7e775690fbb.js.map +1 -0
- package/dist/public/loan-object.js +1 -1
- package/dist/public/loan-object.js.br +0 -0
- package/dist/public/loan-object.js.gz +0 -0
- package/dist/public/loan-object.js.map +1 -1
- package/dist/public/popup-focus-host.html +1 -0
- package/dist/public/popup-focus-host.js +6 -0
- package/dist/public/popup-focus-host.js.br +0 -0
- package/dist/public/popup-focus-host.js.gz +0 -0
- package/dist/public/popup-focus-host.js.map +1 -0
- package/dist/public/v1-guest-v2-host.html +1 -1
- package/dist/public/v2-host-v1-guest.html +1 -1
- package/dist/types/lib/host.d.ts +1 -0
- package/dist/types/lib/ihost.d.ts +15 -0
- package/dist/types/lib/tests/timingDedup.test.d.ts +1 -0
- package/dist/types/lib/utils.d.ts +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/umd/callchain-host.html +34 -0
- package/dist/umd/callchain-host.js +3 -0
- package/dist/umd/callchain-host.js.br +0 -0
- package/dist/umd/callchain-host.js.gz +0 -0
- package/dist/umd/callchain-host.js.map +1 -0
- package/dist/umd/callchain-intermediate.html +1 -0
- package/dist/umd/e2e-host.html +5 -0
- package/dist/umd/e2e-host.js +8 -0
- package/dist/umd/e2e-host.js.br +0 -0
- package/dist/umd/e2e-host.js.gz +0 -0
- package/dist/umd/e2e-host.js.map +1 -0
- package/dist/umd/e2e-index.html +1 -0
- package/dist/umd/index.html +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.br +0 -0
- package/dist/umd/index.js.gz +0 -0
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/init.js +1 -1
- package/dist/umd/init.js.br +0 -0
- package/dist/umd/init.js.gz +0 -0
- package/dist/umd/init.js.map +1 -1
- package/dist/umd/loan-object.js +1 -1
- package/dist/umd/loan-object.js.br +0 -0
- package/dist/umd/loan-object.js.gz +0 -0
- package/dist/umd/loan-object.js.map +1 -1
- package/dist/umd/popup-focus-host.html +1 -0
- package/dist/umd/popup-focus-host.js +6 -0
- package/dist/umd/popup-focus-host.js.br +0 -0
- package/dist/umd/popup-focus-host.js.gz +0 -0
- package/dist/umd/popup-focus-host.js.map +1 -0
- package/dist/umd/v2-host-v1-guest.html +1 -1
- package/package.json +5 -5
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js +0 -3
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.br +0 -0
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.gz +0 -0
- package/dist/public/js/emuiSsfHost.a4526c5eda64df08190f.js.map +0 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>SSF E2E Test Suite</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com?plugins=forms"></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body class="bg-gray-50">
|
|
10
|
+
<header class="bg-gray-900 text-white px-6 py-4">
|
|
11
|
+
<h1 class="text-xl font-bold">SSF End-to-End Test Suite</h1>
|
|
12
|
+
<p class="text-sm text-gray-400 mt-1">
|
|
13
|
+
Comprehensive blackbox tests for SSF Host & Guest. Each page
|
|
14
|
+
includes test steps, expected values, and a verification checklist.
|
|
15
|
+
</p>
|
|
16
|
+
</header>
|
|
17
|
+
|
|
18
|
+
<main class="mx-auto max-w-5xl px-6 py-6">
|
|
19
|
+
<!-- Setup -->
|
|
20
|
+
<section
|
|
21
|
+
class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4"
|
|
22
|
+
>
|
|
23
|
+
<h2 class="text-sm font-semibold text-yellow-800 mb-2">
|
|
24
|
+
Setup Instructions
|
|
25
|
+
</h2>
|
|
26
|
+
<ol class="text-xs text-yellow-700 list-decimal ml-4 space-y-1">
|
|
27
|
+
<li>
|
|
28
|
+
Install dependencies:
|
|
29
|
+
<code class="bg-yellow-100 px-1 rounded">pnpm install</code>
|
|
30
|
+
</li>
|
|
31
|
+
<li>
|
|
32
|
+
Start SSF Host dev server:
|
|
33
|
+
<code class="bg-yellow-100 px-1 rounded"
|
|
34
|
+
>pnpm nx run ssf-host:start-server</code
|
|
35
|
+
>
|
|
36
|
+
(port 4000)
|
|
37
|
+
</li>
|
|
38
|
+
<li>
|
|
39
|
+
Start SSF Guest dev server:
|
|
40
|
+
<code class="bg-yellow-100 px-1 rounded"
|
|
41
|
+
>pnpm nx run ssf-guest:start</code
|
|
42
|
+
>
|
|
43
|
+
(port 4001)
|
|
44
|
+
</li>
|
|
45
|
+
<li>
|
|
46
|
+
Open
|
|
47
|
+
<code class="bg-yellow-100 px-1 rounded"
|
|
48
|
+
>http://localhost:4000/e2e-index.html</code
|
|
49
|
+
>
|
|
50
|
+
in the browser
|
|
51
|
+
</li>
|
|
52
|
+
<li>
|
|
53
|
+
Each test page has
|
|
54
|
+
<code class="bg-yellow-100 px-1 rounded">data-testid</code>
|
|
55
|
+
attributes for Playwright/Cypress selectors
|
|
56
|
+
</li>
|
|
57
|
+
</ol>
|
|
58
|
+
</section>
|
|
59
|
+
|
|
60
|
+
<!-- 1. Core Host-Guest Communication -->
|
|
61
|
+
<section class="mb-6">
|
|
62
|
+
<h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
|
|
63
|
+
1. Core Host-Guest Communication
|
|
64
|
+
</h2>
|
|
65
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
66
|
+
<a
|
|
67
|
+
href="./e2e-host.html"
|
|
68
|
+
data-testid="link-e2e-host"
|
|
69
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
70
|
+
>
|
|
71
|
+
<h3 class="font-medium text-indigo-700">Comprehensive E2E Host</h3>
|
|
72
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
73
|
+
All host-guest scenarios: guest loading, scripting objects,
|
|
74
|
+
events, lifecycle, metadata, sandbox, guest-scoped objects.
|
|
75
|
+
</p>
|
|
76
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
77
|
+
<strong>What to verify:</strong> Guests load in iframes/popups.
|
|
78
|
+
Scripting objects can be added/removed/invoked. Events reach
|
|
79
|
+
subscribed guests. Unload/close cleans up properly. Sandbox attrs
|
|
80
|
+
applied.
|
|
81
|
+
</p>
|
|
82
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
83
|
+
<strong>Test Cases:</strong>
|
|
84
|
+
TC-LOAD-01..05 • TC-SO-01..08 • TC-EVT-01..04 •
|
|
85
|
+
TC-LIFE-01..03 • TC-META-01 • TC-SB-01..03
|
|
86
|
+
</div>
|
|
87
|
+
</a>
|
|
88
|
+
<a
|
|
89
|
+
href="./index.html"
|
|
90
|
+
data-testid="link-main-demo"
|
|
91
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
92
|
+
>
|
|
93
|
+
<h3 class="font-medium text-indigo-700">Main Demo App</h3>
|
|
94
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
95
|
+
Original Loan Application demo with embedded and popup guests,
|
|
96
|
+
events, scripting objects.
|
|
97
|
+
</p>
|
|
98
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
99
|
+
<strong>What to verify:</strong> Loan form data flows to guests.
|
|
100
|
+
Events (onLoanAmountChanged, etc.) update guest UIs. Popup guests
|
|
101
|
+
open and interact with the Loan object.
|
|
102
|
+
</p>
|
|
103
|
+
</a>
|
|
104
|
+
</div>
|
|
105
|
+
</section>
|
|
106
|
+
|
|
107
|
+
<!-- 2. CallChain & Metadata -->
|
|
108
|
+
<section class="mb-6">
|
|
109
|
+
<h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
|
|
110
|
+
2. Call Chain & Metadata Propagation
|
|
111
|
+
</h2>
|
|
112
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
113
|
+
<a
|
|
114
|
+
href="./callchain-host.html"
|
|
115
|
+
data-testid="link-callchain"
|
|
116
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
117
|
+
>
|
|
118
|
+
<h3 class="font-medium text-indigo-700">
|
|
119
|
+
Nested CallChain Test (A → B → C)
|
|
120
|
+
</h3>
|
|
121
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
122
|
+
Root Host (A) → Intermediate Guest+Host (B) → Grandchild Guest
|
|
123
|
+
(C). Verifies callContext.callChain and metadata propagation.
|
|
124
|
+
</p>
|
|
125
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
126
|
+
<strong>What to verify:</strong> Click buttons in Grandchild C.
|
|
127
|
+
Host A should show <code>callContext.guest</code> = B and
|
|
128
|
+
<code>callChain</code> containing C and B with their metadata.
|
|
129
|
+
Return values should flow back to C.
|
|
130
|
+
</p>
|
|
131
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
132
|
+
<strong>Test Cases:</strong> TC-CC-01..07
|
|
133
|
+
</div>
|
|
134
|
+
</a>
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<!-- 3. Popup Behavior -->
|
|
139
|
+
<section class="mb-6">
|
|
140
|
+
<h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
|
|
141
|
+
3. Popup Guest Behavior
|
|
142
|
+
</h2>
|
|
143
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
144
|
+
<a
|
|
145
|
+
href="./popup-focus-host.html"
|
|
146
|
+
data-testid="link-popup-focus"
|
|
147
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
148
|
+
>
|
|
149
|
+
<h3 class="font-medium text-indigo-700">
|
|
150
|
+
Popup Focus & Lifecycle
|
|
151
|
+
</h3>
|
|
152
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
153
|
+
Open, close, and refocus popup guests. Tests trusted vs untrusted
|
|
154
|
+
domain behavior.
|
|
155
|
+
</p>
|
|
156
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
157
|
+
<strong>What to verify:</strong> Trusted popups (localhost):
|
|
158
|
+
re-open brings existing popup to front. Untrusted popups: opener
|
|
159
|
+
is nulled, focus falls back to WindowProxy. Closed popup detection
|
|
160
|
+
updates guest status.
|
|
161
|
+
</p>
|
|
162
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
163
|
+
<strong>Test Cases:</strong> TC-POP-01..11
|
|
164
|
+
</div>
|
|
165
|
+
</a>
|
|
166
|
+
</div>
|
|
167
|
+
</section>
|
|
168
|
+
|
|
169
|
+
<!-- 4. V1/V2 Interoperability -->
|
|
170
|
+
<section class="mb-6">
|
|
171
|
+
<h2 class="text-lg font-semibold text-gray-800 border-b pb-1 mb-3">
|
|
172
|
+
4. V1 / V2 Interoperability
|
|
173
|
+
</h2>
|
|
174
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
175
|
+
<a
|
|
176
|
+
href="./v2-host-v1-guest.html"
|
|
177
|
+
data-testid="link-v2-host-v1-guest"
|
|
178
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
179
|
+
>
|
|
180
|
+
<h3 class="font-medium text-indigo-700">V2 Host → V1 Guest</h3>
|
|
181
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
182
|
+
V2 host loads V1 guest. Loan object, save flow, event feedback.
|
|
183
|
+
</p>
|
|
184
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
185
|
+
<strong>What to verify:</strong> V1 guest connects, gets objects,
|
|
186
|
+
subscribes to events, and returns feedback correctly.
|
|
187
|
+
</p>
|
|
188
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
189
|
+
<strong>Test Cases:</strong> TC-INTEROP-01..04
|
|
190
|
+
</div>
|
|
191
|
+
</a>
|
|
192
|
+
<a
|
|
193
|
+
href="./v2-host-v1-guest.html?nestV1GuestV2Host=true"
|
|
194
|
+
data-testid="link-v2-v1-v2-chain"
|
|
195
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
196
|
+
>
|
|
197
|
+
<h3 class="font-medium text-indigo-700">
|
|
198
|
+
V2 Host → V1 Guest → V2 Host → V2 Guest
|
|
199
|
+
</h3>
|
|
200
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
201
|
+
Full mixed-version chain. V1 guest acts as intermediate host for a
|
|
202
|
+
V2 grandchild.
|
|
203
|
+
</p>
|
|
204
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
205
|
+
<strong>What to verify:</strong> Scripting objects are cloned
|
|
206
|
+
across V1/V2 boundary. Events flow through all layers.
|
|
207
|
+
</p>
|
|
208
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
209
|
+
<strong>Test Cases:</strong> TC-INTEROP-05..07
|
|
210
|
+
</div>
|
|
211
|
+
</a>
|
|
212
|
+
</div>
|
|
213
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
|
|
214
|
+
<a
|
|
215
|
+
href="./v1-host.html"
|
|
216
|
+
data-testid="link-v1-host"
|
|
217
|
+
class="block bg-white rounded-lg shadow p-4 hover:shadow-md transition"
|
|
218
|
+
>
|
|
219
|
+
<h3 class="font-medium text-indigo-700">
|
|
220
|
+
V1 Host → V1 Guest → V2 Host → V2 Guest
|
|
221
|
+
</h3>
|
|
222
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
223
|
+
Starting from V1 host. Exercises the full V1-to-V2 upgrade path.
|
|
224
|
+
</p>
|
|
225
|
+
<p class="text-xs text-gray-400 mt-2">
|
|
226
|
+
<strong>What to verify:</strong> V1 host initializes. V1 guest
|
|
227
|
+
connects. V2 host/guest chain works through V1 intermediate.
|
|
228
|
+
</p>
|
|
229
|
+
</a>
|
|
230
|
+
</div>
|
|
231
|
+
</section>
|
|
232
|
+
</main>
|
|
233
|
+
</body>
|
|
234
|
+
</html>
|
package/dist/esm/host.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
OpenMode
|
|
14
14
|
} from "./types.js";
|
|
15
15
|
import { Guest } from "./guest.js";
|
|
16
|
-
import { flatten, isFunction } from "./utils.js";
|
|
16
|
+
import { flatten, isFunction, isTrustedDomain } from "./utils.js";
|
|
17
17
|
const SANDBOX_DEFAULT = [
|
|
18
18
|
IFrameSandboxValues.AllowScripts,
|
|
19
19
|
IFrameSandboxValues.AllowPopups,
|
|
@@ -80,6 +80,21 @@ class SSFHost {
|
|
|
80
80
|
* callback for guest event unsubscription
|
|
81
81
|
*/
|
|
82
82
|
#onGuestEventUnsubscribe = null;
|
|
83
|
+
/**
|
|
84
|
+
* host-supplied metadata keyed by guest id, forwarded in callChain
|
|
85
|
+
*/
|
|
86
|
+
#guestMetadata = /* @__PURE__ */ new Map();
|
|
87
|
+
/**
|
|
88
|
+
* Tracks the last time a timing measurement was recorded for each metric name.
|
|
89
|
+
* Used to skip redundant startTiming/endTiming cross-window calls for
|
|
90
|
+
* the same API or event within the dedup window.
|
|
91
|
+
*/
|
|
92
|
+
#timingLastRecorded = /* @__PURE__ */ new Map();
|
|
93
|
+
/**
|
|
94
|
+
* Minimum interval (ms) between timing measurements for the same metric name.
|
|
95
|
+
*/
|
|
96
|
+
#timingDedupWindowMs;
|
|
97
|
+
static DEFAULT_TIMING_DEDUP_WINDOW_MS = 1e4;
|
|
83
98
|
/**
|
|
84
99
|
* Create a new host
|
|
85
100
|
* @param hostId unique identifier for the host
|
|
@@ -91,6 +106,7 @@ class SSFHost {
|
|
|
91
106
|
if (!option?.analyticsObj) throw new Error("Analytics object is required");
|
|
92
107
|
this.#logger = option.logger;
|
|
93
108
|
this.#analyticsObj = option.analyticsObj;
|
|
109
|
+
this.#timingDedupWindowMs = option?.timingDedupWindowMs ?? SSFHost.DEFAULT_TIMING_DEDUP_WINDOW_MS;
|
|
94
110
|
this.#correlationId = uuidv4();
|
|
95
111
|
this.#remoting = new Remoting(this.#logger, this.#correlationId);
|
|
96
112
|
if (option?.readyStateCallback && typeof option?.readyStateCallback !== "function")
|
|
@@ -126,6 +142,22 @@ class SSFHost {
|
|
|
126
142
|
this.#logger.debug(`Analytics endTiming failed: ${e.message}`);
|
|
127
143
|
});
|
|
128
144
|
};
|
|
145
|
+
/**
|
|
146
|
+
* Returns true if a timing measurement should be recorded for the given
|
|
147
|
+
* metric name. Skips the measurement when one was already recorded for the
|
|
148
|
+
* same name within the configured dedup window ({@link HostOption.timingDedupWindowMs}).
|
|
149
|
+
* Always returns true when dedup is disabled (window = 0).
|
|
150
|
+
* @param name
|
|
151
|
+
*/
|
|
152
|
+
#shouldTrackTiming = (name) => {
|
|
153
|
+
if (this.#timingDedupWindowMs <= 0) return true;
|
|
154
|
+
const now = performance.now();
|
|
155
|
+
const last = this.#timingLastRecorded.get(name);
|
|
156
|
+
if (last !== void 0 && now - last < this.#timingDedupWindowMs)
|
|
157
|
+
return false;
|
|
158
|
+
this.#timingLastRecorded.set(name, now);
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
129
161
|
#closeAllPopupGuests = () => {
|
|
130
162
|
const popupIds = Array.from(this.#guests.values()).filter((guest) => guest.openMode === OpenMode.Popup).map((guest) => guest.id);
|
|
131
163
|
popupIds.forEach((id) => this.unloadGuest(id));
|
|
@@ -191,7 +223,8 @@ class SSFHost {
|
|
|
191
223
|
guest,
|
|
192
224
|
obj,
|
|
193
225
|
functionName,
|
|
194
|
-
functionParams
|
|
226
|
+
functionParams,
|
|
227
|
+
callerChain
|
|
195
228
|
}) => {
|
|
196
229
|
const func = obj[functionName];
|
|
197
230
|
if (!isFunction(func)) {
|
|
@@ -209,7 +242,10 @@ class SSFHost {
|
|
|
209
242
|
);
|
|
210
243
|
return new Promise((resolve) => {
|
|
211
244
|
Object.defineProperty(func, "callContext", {
|
|
212
|
-
value: {
|
|
245
|
+
value: {
|
|
246
|
+
guest,
|
|
247
|
+
...callerChain?.length ? { callChain: callerChain } : {}
|
|
248
|
+
},
|
|
213
249
|
configurable: true,
|
|
214
250
|
enumerable: true,
|
|
215
251
|
writable: true
|
|
@@ -322,7 +358,7 @@ class SSFHost {
|
|
|
322
358
|
requestId,
|
|
323
359
|
response: objects
|
|
324
360
|
});
|
|
325
|
-
this.#logger.
|
|
361
|
+
this.#logger.debug({
|
|
326
362
|
message: `name of scripting objects returned`,
|
|
327
363
|
requestId,
|
|
328
364
|
objects,
|
|
@@ -365,7 +401,7 @@ class SSFHost {
|
|
|
365
401
|
requestId,
|
|
366
402
|
response: this.#encodeResponse(obj, guest)
|
|
367
403
|
});
|
|
368
|
-
this.#logger.
|
|
404
|
+
this.#logger.debug({
|
|
369
405
|
message: `Scripting Object returned`,
|
|
370
406
|
requestId,
|
|
371
407
|
scriptingObject: objectId,
|
|
@@ -492,7 +528,7 @@ class SSFHost {
|
|
|
492
528
|
requestId,
|
|
493
529
|
body
|
|
494
530
|
}) => {
|
|
495
|
-
const { objectId } = body;
|
|
531
|
+
const { objectId, callerChain } = body;
|
|
496
532
|
const guest = this.#getGuestForWindow(sourceWin);
|
|
497
533
|
if (!guest) {
|
|
498
534
|
this.#logger.warn(
|
|
@@ -517,15 +553,20 @@ class SSFHost {
|
|
|
517
553
|
return false;
|
|
518
554
|
}
|
|
519
555
|
const guestInfo = guest.getInfo();
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
556
|
+
const timingName = `ScriptingObject.API.${objectId}.${body.functionName}`;
|
|
557
|
+
const trackTiming = this.#shouldTrackTiming(timingName);
|
|
558
|
+
if (trackTiming) {
|
|
559
|
+
this.#startTiming(timingName, {
|
|
560
|
+
appId: guestInfo.guestId,
|
|
561
|
+
appUrl: guestInfo.guestUrl
|
|
562
|
+
});
|
|
563
|
+
}
|
|
524
564
|
this.#invoke({
|
|
525
565
|
guest,
|
|
526
566
|
obj,
|
|
527
567
|
functionName: body.functionName,
|
|
528
|
-
functionParams: body.functionParams
|
|
568
|
+
functionParams: body.functionParams,
|
|
569
|
+
callerChain
|
|
529
570
|
}).then((val) => {
|
|
530
571
|
this.#remoting.respond({
|
|
531
572
|
targetWin: sourceWin,
|
|
@@ -533,7 +574,7 @@ class SSFHost {
|
|
|
533
574
|
requestId,
|
|
534
575
|
response: this.#encodeResponse(val, guest)
|
|
535
576
|
});
|
|
536
|
-
this.#logger.
|
|
577
|
+
this.#logger.debug({
|
|
537
578
|
message: `Value returned for Scripting Object method call`,
|
|
538
579
|
requestId,
|
|
539
580
|
scriptingObject: objectId,
|
|
@@ -547,7 +588,7 @@ class SSFHost {
|
|
|
547
588
|
requestId,
|
|
548
589
|
ex
|
|
549
590
|
});
|
|
550
|
-
this.#logger.
|
|
591
|
+
this.#logger.error({
|
|
551
592
|
message: `Exception thrown for Scripting Object method call`,
|
|
552
593
|
requestId,
|
|
553
594
|
scriptingObject: objectId,
|
|
@@ -555,13 +596,12 @@ class SSFHost {
|
|
|
555
596
|
...guestInfo
|
|
556
597
|
});
|
|
557
598
|
}).finally(() => {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
{
|
|
599
|
+
if (trackTiming) {
|
|
600
|
+
this.#endTiming(timingName, {
|
|
561
601
|
appId: guestInfo.guestId,
|
|
562
602
|
appUrl: guestInfo.guestUrl
|
|
563
|
-
}
|
|
564
|
-
|
|
603
|
+
});
|
|
604
|
+
}
|
|
565
605
|
});
|
|
566
606
|
return true;
|
|
567
607
|
};
|
|
@@ -704,10 +744,11 @@ class SSFHost {
|
|
|
704
744
|
let guest = this.#getGuestForUrl(url);
|
|
705
745
|
if (guest) {
|
|
706
746
|
if (!guest.window.closed) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
747
|
+
if (isTrustedDomain(url)) {
|
|
748
|
+
window.open("", title);
|
|
749
|
+
} else {
|
|
750
|
+
guest.window.focus();
|
|
751
|
+
}
|
|
711
752
|
}
|
|
712
753
|
} else {
|
|
713
754
|
const windowFeatures = Object.entries({ width, height, top, left }).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join(",");
|
|
@@ -734,7 +775,9 @@ class SSFHost {
|
|
|
734
775
|
}
|
|
735
776
|
}, 0);
|
|
736
777
|
}
|
|
737
|
-
|
|
778
|
+
if (!isTrustedDomain(url)) {
|
|
779
|
+
guestWindow.opener = null;
|
|
780
|
+
}
|
|
738
781
|
guest = this.#attachGuest({
|
|
739
782
|
guestId,
|
|
740
783
|
window: guestWindow,
|
|
@@ -901,6 +944,22 @@ class SSFHost {
|
|
|
901
944
|
} else if (isFunction(propValue)) {
|
|
902
945
|
Object.defineProperty(so, propName, {
|
|
903
946
|
value: async (...args) => {
|
|
947
|
+
const callerCtx = so[propName]?.callContext;
|
|
948
|
+
if (callerCtx?.guest) {
|
|
949
|
+
const existingChain = callerCtx.callChain ?? [];
|
|
950
|
+
const guestId = callerCtx.guest.id;
|
|
951
|
+
const meta = this.#guestMetadata.get(guestId);
|
|
952
|
+
const callerEntry = { id: guestId };
|
|
953
|
+
if (meta) callerEntry.metadata = meta;
|
|
954
|
+
Object.defineProperty(propValue, "callContext", {
|
|
955
|
+
value: {
|
|
956
|
+
callChain: [...existingChain, callerEntry]
|
|
957
|
+
},
|
|
958
|
+
configurable: true,
|
|
959
|
+
enumerable: true,
|
|
960
|
+
writable: true
|
|
961
|
+
});
|
|
962
|
+
}
|
|
904
963
|
const retVal = await propValue(...args);
|
|
905
964
|
return isScriptingObjectProxy(retVal) ? this.cloneScriptingObject(retVal) : retVal;
|
|
906
965
|
},
|
|
@@ -935,6 +994,7 @@ class SSFHost {
|
|
|
935
994
|
this.#popupGuestMonitor = null;
|
|
936
995
|
}
|
|
937
996
|
this.#closeAllPopupGuests();
|
|
997
|
+
this.#guestMetadata.clear();
|
|
938
998
|
this.#remoting.close();
|
|
939
999
|
window.removeEventListener("beforeunload", this.#closeAllPopupGuests);
|
|
940
1000
|
this.#logger.debug(
|
|
@@ -985,22 +1045,20 @@ class SSFHost {
|
|
|
985
1045
|
};
|
|
986
1046
|
}
|
|
987
1047
|
const guestPromises = [];
|
|
1048
|
+
const eventTimingName = `ScriptingObject.Event.${scriptingObject.id}.${name}`;
|
|
988
1049
|
let timingMetricStarted = false;
|
|
989
1050
|
const dispatchToGuest = (guest) => {
|
|
990
1051
|
const guestInfo = guest.getInfo();
|
|
991
1052
|
if (timeout && guest?.capabilities?.eventFeedback) {
|
|
992
1053
|
guestPromises.push(guest.dispatchEvent(eventObj, timeout));
|
|
993
|
-
if (!timingMetricStarted) {
|
|
994
|
-
this.#startTiming(
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
appUrl: window.location.href
|
|
999
|
-
}
|
|
1000
|
-
);
|
|
1054
|
+
if (!timingMetricStarted && this.#shouldTrackTiming(eventTimingName)) {
|
|
1055
|
+
this.#startTiming(eventTimingName, {
|
|
1056
|
+
appId: this.hostId,
|
|
1057
|
+
appUrl: window.location.href
|
|
1058
|
+
});
|
|
1001
1059
|
timingMetricStarted = true;
|
|
1002
1060
|
}
|
|
1003
|
-
this.#logger.
|
|
1061
|
+
this.#logger.debug({
|
|
1004
1062
|
message: "Event dispatched and awaiting feedback",
|
|
1005
1063
|
scriptingEventId: id,
|
|
1006
1064
|
...guestInfo
|
|
@@ -1010,7 +1068,7 @@ class SSFHost {
|
|
|
1010
1068
|
messageType: MessageType.ObjectEvent,
|
|
1011
1069
|
messageBody: eventObj
|
|
1012
1070
|
});
|
|
1013
|
-
this.#logger.
|
|
1071
|
+
this.#logger.debug({
|
|
1014
1072
|
message: "Event dispatched",
|
|
1015
1073
|
scriptingEventId: id,
|
|
1016
1074
|
...guestInfo
|
|
@@ -1022,8 +1080,8 @@ class SSFHost {
|
|
|
1022
1080
|
} else {
|
|
1023
1081
|
this.#guests.forEach(dispatchToGuest);
|
|
1024
1082
|
}
|
|
1025
|
-
|
|
1026
|
-
this.#logger.
|
|
1083
|
+
return Promise.all(guestPromises).then((values) => {
|
|
1084
|
+
this.#logger.debug({
|
|
1027
1085
|
message: "Event feedback received",
|
|
1028
1086
|
scriptingEventId: id
|
|
1029
1087
|
});
|
|
@@ -1037,15 +1095,11 @@ class SSFHost {
|
|
|
1037
1095
|
throw ex;
|
|
1038
1096
|
}).finally(() => {
|
|
1039
1097
|
if (timingMetricStarted)
|
|
1040
|
-
this.#endTiming(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
appUrl: window.location.href
|
|
1045
|
-
}
|
|
1046
|
-
);
|
|
1098
|
+
this.#endTiming(eventTimingName, {
|
|
1099
|
+
appId: this.hostId,
|
|
1100
|
+
appUrl: window.location.href
|
|
1101
|
+
});
|
|
1047
1102
|
});
|
|
1048
|
-
return retValue;
|
|
1049
1103
|
};
|
|
1050
1104
|
/**
|
|
1051
1105
|
* get reference to all guest applications
|
|
@@ -1073,7 +1127,8 @@ class SSFHost {
|
|
|
1073
1127
|
searchParams = {},
|
|
1074
1128
|
onLoad,
|
|
1075
1129
|
onError,
|
|
1076
|
-
options = {}
|
|
1130
|
+
options = {},
|
|
1131
|
+
metadata
|
|
1077
1132
|
} = param;
|
|
1078
1133
|
if (!guestId) throw new Error("id for guest application is required");
|
|
1079
1134
|
let instanceId = guestId;
|
|
@@ -1115,6 +1170,7 @@ class SSFHost {
|
|
|
1115
1170
|
} else {
|
|
1116
1171
|
throw new Error(`Invalid openMode: ${openMode}`);
|
|
1117
1172
|
}
|
|
1173
|
+
if (metadata) this.#guestMetadata.set(instanceId, metadata);
|
|
1118
1174
|
this.#logger.audit({
|
|
1119
1175
|
message: "Guest loaded",
|
|
1120
1176
|
...guest.getInfo()
|
|
@@ -1180,6 +1236,7 @@ class SSFHost {
|
|
|
1180
1236
|
guest.dispose();
|
|
1181
1237
|
this.#guestsByWindow.delete(guest.window);
|
|
1182
1238
|
this.#guestsByUrl.delete(guest.url);
|
|
1239
|
+
this.#guestMetadata.delete(guest.id);
|
|
1183
1240
|
this.#guests.delete(guest.id);
|
|
1184
1241
|
this.#logger.audit({
|
|
1185
1242
|
message: `Guest is removed from host`,
|