@newrelic/browser-agent 1.319.0 → 1.320.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/CHANGELOG.md +13 -0
- package/dist/cjs/common/config/init.js +17 -2
- package/dist/cjs/common/config/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/iframe-constants.js +16 -0
- package/dist/cjs/common/url/add-url.js +23 -0
- package/dist/cjs/common/util/console.js +40 -33
- package/dist/cjs/common/v2/mfe-vitals.js +9 -2
- package/dist/cjs/common/v2/script-correlation.js +2 -0
- package/dist/cjs/common/v2/script-tracker-constants.js +13 -0
- package/dist/cjs/common/v2/script-tracker.js +54 -29
- package/dist/cjs/common/v2/timing-factory.js +18 -0
- package/dist/cjs/common/v2/utils.js +100 -6
- package/dist/cjs/features/ajax/instrument/index.js +4 -14
- package/dist/cjs/features/utils/instrument-base.js +13 -0
- package/dist/cjs/interfaces/registered-iframe-entity.js +527 -0
- package/dist/cjs/loaders/api/register.js +10 -6
- package/dist/cjs/loaders/configure/configure.js +14 -0
- package/dist/cjs/loaders/configure/iframe-message-handler.js +270 -0
- package/dist/esm/common/config/init.js +17 -2
- package/dist/esm/common/config/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/iframe-constants.js +10 -0
- package/dist/esm/common/url/add-url.js +16 -0
- package/dist/esm/common/util/console.js +40 -33
- package/dist/esm/common/v2/mfe-vitals.js +9 -2
- package/dist/esm/common/v2/script-correlation.js +2 -0
- package/dist/esm/common/v2/script-tracker-constants.js +7 -0
- package/dist/esm/common/v2/script-tracker.js +54 -29
- package/dist/esm/common/v2/timing-factory.js +12 -0
- package/dist/esm/common/v2/utils.js +97 -6
- package/dist/esm/features/ajax/instrument/index.js +1 -11
- package/dist/esm/features/utils/instrument-base.js +13 -0
- package/dist/esm/interfaces/registered-iframe-entity.js +518 -0
- package/dist/esm/loaders/api/register.js +10 -6
- package/dist/esm/loaders/configure/configure.js +15 -1
- package/dist/esm/loaders/configure/iframe-message-handler.js +263 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/iframe-constants.d.ts +11 -0
- package/dist/types/common/constants/iframe-constants.d.ts.map +1 -0
- package/dist/types/common/url/add-url.d.ts +2 -0
- package/dist/types/common/url/add-url.d.ts.map +1 -0
- package/dist/types/common/util/console.d.ts +40 -33
- package/dist/types/common/util/console.d.ts.map +1 -1
- package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -1
- package/dist/types/common/v2/script-correlation.d.ts +2 -0
- package/dist/types/common/v2/script-correlation.d.ts.map +1 -1
- package/dist/types/common/v2/script-tracker-constants.d.ts +7 -0
- package/dist/types/common/v2/script-tracker-constants.d.ts.map +1 -0
- package/dist/types/common/v2/script-tracker.d.ts +4 -1
- package/dist/types/common/v2/script-tracker.d.ts.map +1 -1
- package/dist/types/common/v2/timing-factory.d.ts +9 -0
- package/dist/types/common/v2/timing-factory.d.ts.map +1 -0
- package/dist/types/common/v2/utils.d.ts +37 -0
- package/dist/types/common/v2/utils.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/interfaces/registered-iframe-entity.d.ts +116 -0
- package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/configure/iframe-message-handler.d.ts +17 -0
- package/dist/types/loaders/configure/iframe-message-handler.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/common/config/init.js +13 -2
- package/src/common/config/runtime.js +2 -1
- package/src/common/constants/iframe-constants.js +11 -0
- package/src/common/url/add-url.js +18 -0
- package/src/common/util/console.js +40 -33
- package/src/common/v2/mfe-vitals.js +7 -4
- package/src/common/v2/script-correlation.js +2 -0
- package/src/common/v2/script-tracker-constants.js +7 -0
- package/src/common/v2/script-tracker.js +65 -26
- package/src/common/v2/timing-factory.js +10 -0
- package/src/common/v2/utils.js +102 -6
- package/src/features/ajax/instrument/index.js +1 -13
- package/src/features/utils/instrument-base.js +12 -0
- package/src/interfaces/registered-iframe-entity.js +482 -0
- package/src/loaders/api/register.js +9 -5
- package/src/loaders/configure/configure.js +15 -1
- package/src/loaders/configure/iframe-message-handler.js +244 -0
|
@@ -78,11 +78,11 @@ function register (agentRef, target) {
|
|
|
78
78
|
target.blocked = false
|
|
79
79
|
if (typeof target.tags !== 'object' || target.tags === null || Array.isArray(target.tags)) target.tags = {}
|
|
80
80
|
target.parent ??= {
|
|
81
|
-
get id () { return agentRef.runtime.appMetadata.agents[0].entityGuid }, // getter because this is
|
|
81
|
+
get id () { return agentRef.runtime.appMetadata.agents?.[0].entityGuid }, // getter because this is asynchronously set
|
|
82
82
|
type: V2_TYPES.BA
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const timings = findScriptTimings()
|
|
85
|
+
const timings = findScriptTimings(target)
|
|
86
86
|
|
|
87
87
|
// Track MFE vitals for this entity
|
|
88
88
|
const vitals = trackMFEVitals(target, timings)
|
|
@@ -154,7 +154,10 @@ function register (agentRef, target) {
|
|
|
154
154
|
get customAttributes () { return attrs },
|
|
155
155
|
target,
|
|
156
156
|
timings,
|
|
157
|
-
vitals
|
|
157
|
+
vitals,
|
|
158
|
+
events: {
|
|
159
|
+
latestTimestamp: undefined
|
|
160
|
+
}
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
163
|
|
|
@@ -234,8 +237,9 @@ function register (agentRef, target) {
|
|
|
234
237
|
const report = (methodToCall, args, target) => {
|
|
235
238
|
/** Even if we are blocked, if registering we should still return a child register API so nested API calls do not throw errors */
|
|
236
239
|
if (isBlocked() && methodToCall !== register) return
|
|
237
|
-
/**
|
|
238
|
-
const timestamp = now()
|
|
240
|
+
/** use the timestamp captured inside the iframe for this call, if one was supplied (see iframe-message-handler.js); otherwise fall back to now(). Consume it immediately so a stale value can't leak into a later call that isn't preceded by a fresh iframe message (e.g. deregister() via page unload) */
|
|
241
|
+
const timestamp = api.metadata.events.latestTimestamp ?? now()
|
|
242
|
+
api.metadata.events.latestTimestamp = undefined
|
|
239
243
|
const methodName = METHOD_NAMES.get(methodToCall) || 'unknown'
|
|
240
244
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/register/${methodName}/called`], undefined, FEATURE_NAMES.metrics, agentRef.ee)
|
|
241
245
|
try {
|
|
@@ -7,11 +7,13 @@ import { gosCDN } from '../../common/window/nreum'
|
|
|
7
7
|
import { mergeInfo } from '../../common/config/info'
|
|
8
8
|
import { mergeInit } from '../../common/config/init'
|
|
9
9
|
import { mergeRuntime } from '../../common/config/runtime'
|
|
10
|
-
import { isWorkerScope } from '../../common/constants/runtime'
|
|
10
|
+
import { isWorkerScope, globalScope } from '../../common/constants/runtime'
|
|
11
11
|
import { redefinePublicPath } from './public-path'
|
|
12
12
|
import { ee } from '../../common/event-emitter/contextual-ee'
|
|
13
13
|
import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
|
|
14
14
|
import { mergeLoaderConfig } from '../../common/config/loader-config'
|
|
15
|
+
import { handle } from '../../common/event-emitter/handle'
|
|
16
|
+
import { prefix as iframePrefix } from '../../common/constants/iframe-constants'
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Sets or re-sets the agent's configuration values from global settings. This also attach those as properties to the agent instance.
|
|
@@ -81,6 +83,18 @@ export function configure (agent, opts = {}, loaderType, forceDrain) {
|
|
|
81
83
|
data: agent.config
|
|
82
84
|
})
|
|
83
85
|
|
|
86
|
+
// Set up iframe postMessage listener for registered entities
|
|
87
|
+
if (agent.init.api.register.allow_iframe_bridge) {
|
|
88
|
+
globalScope.addEventListener('message', (event) => {
|
|
89
|
+
// Pre-filter here rather than in setupIframeMFEMessageListener, so unrelated MessageEvents
|
|
90
|
+
// (there can be many, from any script on the page) aren't buffered/held onto in memory
|
|
91
|
+
// while waiting for the iframe bridge's lazy chunk to load and drain the buffer.
|
|
92
|
+
if (typeof event.data?.type === 'string' && event.data.type.startsWith(iframePrefix)) {
|
|
93
|
+
handle('iframe-message', [event], undefined, 'IFRAME', agent.ee)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
agent.runtime.configured = true
|
|
85
99
|
}
|
|
86
100
|
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { warn } from '../../common/util/console'
|
|
6
|
+
import { getRegisteredEntityByIframeInterfaceId } from '../../common/v2/utils'
|
|
7
|
+
import { IFRAME_TIMING_UPDATE, IFRAME_API, IFRAME_API_RESPONSE, IFRAME_VITALS_UPDATE, IFRAME_AJAX } from '../../common/constants/iframe-constants'
|
|
8
|
+
import { REGISTER } from '../api/constants'
|
|
9
|
+
import { handle } from '../../common/event-emitter/handle'
|
|
10
|
+
import { FEATURE_NAMES } from '../features/features'
|
|
11
|
+
import { stringify } from '../../common/util/stringify'
|
|
12
|
+
import { registerHandler } from '../../common/event-emitter/register-handler'
|
|
13
|
+
import { drain } from '../../common/drain/drain'
|
|
14
|
+
import { isBrowserScope } from '../../common/constants/runtime'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves the registered entity associated with the iframeInterfaceId from the event data, and validates that the origin of the message event matches the expected origin for that entity.
|
|
18
|
+
* @param {MessageEvent} event - The message event containing the method call or registration request
|
|
19
|
+
* @param {Object} agent - The browser agent instance
|
|
20
|
+
* @returns {Object|undefined} - The registered entity if it exists and the origin is valid, otherwise undefined
|
|
21
|
+
*/
|
|
22
|
+
function getValidEntity (event, agent) {
|
|
23
|
+
try {
|
|
24
|
+
const { iframeInterfaceId } = event.data
|
|
25
|
+
if (!iframeInterfaceId) return
|
|
26
|
+
const entity = getRegisteredEntityByIframeInterfaceId(iframeInterfaceId, agent)
|
|
27
|
+
if (!isValidOrigin(event, entity)) return
|
|
28
|
+
return entity
|
|
29
|
+
} catch (e) {
|
|
30
|
+
// couldnt get entity or validate origin, just let the method return undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isSafeProperty (obj, property) {
|
|
35
|
+
return property in obj && typeof property === 'string' && !['__proto__', 'constructor', 'prototype'].includes(property)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Handles timing property updates from iframes. Mutates the timings object on the entity's metadata with the new value for the specified property.
|
|
40
|
+
* @param {MessageEvent} event - The message event containing the timing update
|
|
41
|
+
* @param {Object} agent - The browser agent instance
|
|
42
|
+
* @returns
|
|
43
|
+
*/
|
|
44
|
+
function handleTimingUpdate (event, agent) {
|
|
45
|
+
const entity = getValidEntity(event, agent)
|
|
46
|
+
if (!entity) return
|
|
47
|
+
|
|
48
|
+
if (!entity.metadata.timings) return
|
|
49
|
+
|
|
50
|
+
event.data.entries?.forEach(({ property, value }) => {
|
|
51
|
+
if (isSafeProperty(entity.metadata.timings, property)) {
|
|
52
|
+
entity.metadata.timings[property] = value
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Handles vitals property updates from iframes. Mutates the vitals object on the entity's metadata with the new value for the specified property.
|
|
59
|
+
* @param {MessageEvent} event - The message event containing the vitals update
|
|
60
|
+
* @param {Object} agent - The browser agent instance
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
function handleVitalsUpdate (event, agent) {
|
|
64
|
+
const entity = getValidEntity(event, agent)
|
|
65
|
+
if (!entity) return
|
|
66
|
+
|
|
67
|
+
if (!entity.metadata?.vitals) return
|
|
68
|
+
|
|
69
|
+
event.data.entries?.forEach(({ property, value }) => {
|
|
70
|
+
if (isSafeProperty(entity.metadata.vitals, property) && (!!Number(value) || value === 0)) {
|
|
71
|
+
entity.metadata.vitals[property].value = value
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Handles ajax events from iframes and forwards them to the agent's event emitter
|
|
78
|
+
* @param {MessageEvent} event - The message event containing the method call or registration request
|
|
79
|
+
* @param {Object} agent - The browser agent instance
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
function handleAjax (event, agent) {
|
|
83
|
+
const entity = getValidEntity(event, agent)
|
|
84
|
+
if (!entity) return
|
|
85
|
+
const { entries } = event.data
|
|
86
|
+
entries?.forEach(({ params, metrics, start, end, initiatorType }) => {
|
|
87
|
+
handle('xhr', [params, metrics, start, end, initiatorType, entity.metadata.target], undefined, FEATURE_NAMES.ajax, agent.ee)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validates that the origin of the message event matches the expected origin for the registered entity
|
|
93
|
+
* @param {MessageEvent} event - The message event containing the method call or registration request
|
|
94
|
+
* @param {Object} entity - The registered entity associated with the iframeInterfaceId from the event data
|
|
95
|
+
* @returns {boolean}
|
|
96
|
+
*/
|
|
97
|
+
function isValidOrigin (event, entity) {
|
|
98
|
+
if (!entity) return false
|
|
99
|
+
try {
|
|
100
|
+
// "null" is the literal serialization the spec uses for every opaque origin (sandboxed iframes
|
|
101
|
+
// without allow-same-origin, data:/javascript: URLs, etc.) -- it does not uniquely identify a
|
|
102
|
+
// single origin, so treating two "null" origins as a match would let an unrelated opaque-origin
|
|
103
|
+
// frame impersonate the one that actually registered.
|
|
104
|
+
if (event.origin === 'null' || entity.metadata.target.iframeOrigin === 'null') return false
|
|
105
|
+
return event.origin === entity.metadata.target.iframeOrigin
|
|
106
|
+
} catch (e) {
|
|
107
|
+
warn(77, e)
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handles both entity registration and method calls on existing entities
|
|
114
|
+
* @async
|
|
115
|
+
* @param {MessageEvent} event - The message event containing the method call or registration request
|
|
116
|
+
* @param {Object} agent - The browser agent instance
|
|
117
|
+
* @returns {Promise<{entity: Object|null, result: any}>}
|
|
118
|
+
*/
|
|
119
|
+
export async function handleMethodCall (event, agent) {
|
|
120
|
+
const { target, iframeInterfaceId, timestamp } = event.data
|
|
121
|
+
// Method calls are always sent one-at-a-time (each has its own postMessage response), so only
|
|
122
|
+
// the first entry is ever populated -- but the payload shape stays consistent with every other
|
|
123
|
+
// iframe message type (timing/vitals/ajax), which all wrap their data in an `entries` array.
|
|
124
|
+
const [{ method, args } = {}] = event.data.entries || []
|
|
125
|
+
const output = { entity: null, result: null }
|
|
126
|
+
// Registration of a new entity needs to be handled differently than method calls on existing entities
|
|
127
|
+
if (method === REGISTER) {
|
|
128
|
+
// "null" is the opaque-origin serialization (sandboxed iframes, data:/javascript: URLs) -- it
|
|
129
|
+
// doesn't identify a single origin, so an entity registered from one can never be safely
|
|
130
|
+
// distinguished from another later on (see isValidOrigin). Reject up front rather than
|
|
131
|
+
// registering an entity that would silently fail every subsequent call.
|
|
132
|
+
if (event.origin === 'null') {
|
|
133
|
+
warn(74, event.origin)
|
|
134
|
+
return output
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const iframeDomains = agent.init.api.register.iframe_domains
|
|
138
|
+
if (iframeDomains.length && !iframeDomains.includes(event.origin)) {
|
|
139
|
+
warn(74, event.origin)
|
|
140
|
+
return output
|
|
141
|
+
}
|
|
142
|
+
const targetData = args?.[0] || target || {}
|
|
143
|
+
if (!agent[REGISTER]) {
|
|
144
|
+
warn(35, REGISTER)
|
|
145
|
+
return output
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const freshTarget = {
|
|
149
|
+
id: targetData.id,
|
|
150
|
+
name: targetData.name,
|
|
151
|
+
type: targetData.type,
|
|
152
|
+
version: targetData.version,
|
|
153
|
+
tags: targetData.tags
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
output.entity = agent[REGISTER](freshTarget)
|
|
157
|
+
output.entity.metadata.target.iframeInterfaceId = iframeInterfaceId
|
|
158
|
+
output.entity.metadata.target.iframeOrigin = event.origin
|
|
159
|
+
|
|
160
|
+
return output
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const entity = getRegisteredEntityByIframeInterfaceId(iframeInterfaceId, agent)
|
|
164
|
+
if (!isValidOrigin(event, entity)) {
|
|
165
|
+
warn(76)
|
|
166
|
+
return output
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
output.entity = entity
|
|
170
|
+
const methodFn = entity[method]
|
|
171
|
+
if (!isSafeProperty(entity, method) || typeof methodFn !== 'function') {
|
|
172
|
+
warn(35, method)
|
|
173
|
+
return output
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Supply the timestamp captured inside the iframe at call time (from event.data.timestamp)
|
|
177
|
+
entity.metadata.events.latestTimestamp = timestamp
|
|
178
|
+
output.result = await methodFn.apply(entity, args || [])
|
|
179
|
+
return output
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Serializes entity metadata for postMessage response.
|
|
184
|
+
* Strips out any non-serializable properties and returns a plain object.
|
|
185
|
+
* postMessage cant handle complex objects that cause DataCloneErrors to throw like functions, circular references, etc.
|
|
186
|
+
* @param {Object} entity - The registered entity whose metadata is to be serialized. Uses a pattern of stringify and parse to remove any non-serializable properties from the metadata object.
|
|
187
|
+
* @returns {Object} - The serialized metadata object
|
|
188
|
+
*/
|
|
189
|
+
function serializeMetadata (entity) {
|
|
190
|
+
const meta = entity.metadata
|
|
191
|
+
|
|
192
|
+
return JSON.parse(stringify(meta))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Sends a postMessage response to the iframe
|
|
197
|
+
* @param {MessageEvent} event - The message event containing the method call or registration request
|
|
198
|
+
* @param {Object} payload - The response payload to send back to the iframe
|
|
199
|
+
*/
|
|
200
|
+
function sendResponse (event, payload) {
|
|
201
|
+
event.source.postMessage({
|
|
202
|
+
type: IFRAME_API_RESPONSE,
|
|
203
|
+
messageId: event.data.messageId,
|
|
204
|
+
iframeInterfaceId: event.data.iframeInterfaceId,
|
|
205
|
+
...payload
|
|
206
|
+
}, event.origin)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Sets up a postMessage listener to handle API calls and timing updates from iframes
|
|
211
|
+
* @param {Object} agent The agent instance
|
|
212
|
+
*/
|
|
213
|
+
export function setupIframeMFEMessageListener (agent) {
|
|
214
|
+
if (!isBrowserScope || !window.addEventListener || agent.runtime.listeningForIframeMessages) return
|
|
215
|
+
|
|
216
|
+
registerHandler('iframe-message', async (event) => {
|
|
217
|
+
if (!event.data) return
|
|
218
|
+
|
|
219
|
+
switch (event.data.type) {
|
|
220
|
+
case IFRAME_TIMING_UPDATE:
|
|
221
|
+
handleTimingUpdate(event, agent)
|
|
222
|
+
return
|
|
223
|
+
case IFRAME_VITALS_UPDATE:
|
|
224
|
+
handleVitalsUpdate(event, agent)
|
|
225
|
+
return
|
|
226
|
+
case IFRAME_AJAX:
|
|
227
|
+
handleAjax(event, agent)
|
|
228
|
+
return
|
|
229
|
+
case IFRAME_API:
|
|
230
|
+
try {
|
|
231
|
+
if (!event.source || !event.data.messageId || !event.data.entries?.[0]?.method) return
|
|
232
|
+
const { entity, result } = await handleMethodCall(event, agent)
|
|
233
|
+
const metadata = entity ? serializeMetadata(entity) : undefined
|
|
234
|
+
sendResponse(event, { result, metadata })
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const errorMessage = error?.message || String(error)
|
|
237
|
+
sendResponse(event, { error: errorMessage })
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}, 'IFRAME', agent.ee)
|
|
241
|
+
|
|
242
|
+
drain(agent, 'IFRAME', true) // drain any buffered iframe messages that were received before the listener was set up
|
|
243
|
+
agent.runtime.listeningForIframeMessages = true
|
|
244
|
+
}
|