@joryio/web-sdk 1.3.0 → 1.3.1-beta.2

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/joryio.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Joryio Web SDK v1.3.0
2
+ * Joryio Web SDK v1.3.1-beta.2
3
3
  * (c) 2026 Joryio. All rights reserved.
4
4
  * @license Commercial - see https://github.com/joryio/joryio-web-sdk/blob/main/LICENSE
5
5
  */
6
- const SDK_VERSION = '1.3.0';
6
+ const SDK_VERSION = '1.3.1';
7
7
  const DEFAULT_API_ENDPOINT = 'https://api-eu1.joryio.com/api';
8
8
  function normalizeApiEndpoint(raw) {
9
9
  const trimmed = (raw || '').trim();
@@ -2261,6 +2261,21 @@ class UTMManager {
2261
2261
  function escapeStyleText(css) {
2262
2262
  return typeof css === 'string' ? css.replace(/<\//g, '<\\/') : '';
2263
2263
  }
2264
+ const IN_APP_DEFAULT_MAX_WIDTH = { modal: 600, slideup: 400 };
2265
+ const IN_APP_VIEWPORT_MARGIN = 16;
2266
+ function inAppMaxWidth(campaign) {
2267
+ var _a;
2268
+ if (campaign.type !== 'modal' && campaign.type !== 'slideup')
2269
+ return null;
2270
+ if (((_a = campaign.content) === null || _a === void 0 ? void 0 : _a.kind) !== 'html')
2271
+ return null;
2272
+ const mw = campaign.content.maxWidth;
2273
+ return typeof mw === 'number' && Number.isFinite(mw) && mw > 0 ? Math.floor(mw) : IN_APP_DEFAULT_MAX_WIDTH[campaign.type];
2274
+ }
2275
+ function inAppFrameWidth(maxWidth, viewportWidth, margin = IN_APP_VIEWPORT_MARGIN) {
2276
+ const available = Math.max(0, Math.floor(viewportWidth) - 2 * margin);
2277
+ return Math.max(0, Math.min(Math.floor(maxWidth), available));
2278
+ }
2264
2279
  const HOST_BRIDGE_KEY = '__joryioInApp';
2265
2280
  const IMPRESSIONS_STORAGE_KEY = 'inapp_impressions';
2266
2281
  const MAX_DISPLAYED_MESSAGES = 500;
@@ -2376,6 +2391,7 @@ class InAppMessagingManager {
2376
2391
  this.syncAgain = false;
2377
2392
  this.claimed = new Set();
2378
2393
  this.iframes = new Map();
2394
+ this.frameMaxWidths = new Map();
2379
2395
  this.allowHtmlJsInAppMessages = false;
2380
2396
  this.bridge = null;
2381
2397
  this.auth = null;
@@ -2495,16 +2511,13 @@ class InAppMessagingManager {
2495
2511
  this.trackImpression(campaignId, 'clicked').catch(() => { });
2496
2512
  return this.submitForm(campaignId, values);
2497
2513
  },
2498
- setSize: (campaignId, width, height) => {
2514
+ setSize: (campaignId, _width, height) => {
2499
2515
  var _a;
2500
2516
  const iframe = this.iframes.get(campaignId);
2501
2517
  if (!iframe || !Number.isFinite(height) || height <= 0)
2502
2518
  return;
2503
2519
  const type = (_a = this.displayedMessages.get(campaignId)) === null || _a === void 0 ? void 0 : _a.campaign.type;
2504
2520
  iframe.style.height = type === 'fullscreen' ? '100%' : `${Math.ceil(height)}px`;
2505
- if ((type === 'modal' || type === 'slideup') && Number.isFinite(width) && width > 0) {
2506
- iframe.style.width = `${Math.ceil(width)}px`;
2507
- }
2508
2521
  },
2509
2522
  };
2510
2523
  }
@@ -3202,6 +3215,13 @@ class InAppMessagingManager {
3202
3215
  if (campaign.type === 'banner' || campaign.type === 'fullscreen') {
3203
3216
  iframe.style.width = '100%';
3204
3217
  }
3218
+ const maxWidth = inAppMaxWidth(campaign);
3219
+ if (maxWidth !== null) {
3220
+ container.style.maxWidth = 'none';
3221
+ this.frameMaxWidths.set(campaign.id, maxWidth);
3222
+ iframe.style.width = `${inAppFrameWidth(maxWidth, this.hostViewportWidth())}px`;
3223
+ this.watchViewport();
3224
+ }
3205
3225
  iframe.srcdoc = this.buildSrcdoc(campaign.id, renderedHtml, ((_d = campaign.content) === null || _d === void 0 ? void 0 : _d.kind) === 'html' ? campaign.content.css : '', campaign.type);
3206
3226
  container.appendChild(iframe);
3207
3227
  this.iframes.set(campaign.id, iframe);
@@ -3255,13 +3275,53 @@ class InAppMessagingManager {
3255
3275
  this.callHost('onMessageDisplay', () => { var _a, _b; return (_b = (_a = this.displayOptions).onDisplay) === null || _b === void 0 ? void 0 : _b.call(_a, message); });
3256
3276
  this.logger.info(`Displayed campaign: ${campaign.name} (${campaign.id})`);
3257
3277
  }
3278
+ hostViewportWidth() {
3279
+ const doc = typeof document !== 'undefined' ? document.documentElement : null;
3280
+ const cw = doc && typeof doc.clientWidth === 'number' ? doc.clientWidth : 0;
3281
+ if (cw > 0)
3282
+ return cw;
3283
+ return typeof window !== 'undefined' && typeof window.innerWidth === 'number' ? window.innerWidth : 0;
3284
+ }
3285
+ sizeContentFrame(campaignId) {
3286
+ const iframe = this.iframes.get(campaignId);
3287
+ const maxWidth = this.frameMaxWidths.get(campaignId);
3288
+ if (!iframe || maxWidth === undefined)
3289
+ return;
3290
+ iframe.style.width = `${inAppFrameWidth(maxWidth, this.hostViewportWidth())}px`;
3291
+ }
3292
+ watchViewport() {
3293
+ if (this.viewportListener || typeof window === 'undefined' || typeof window.addEventListener !== 'function')
3294
+ return;
3295
+ this.viewportListener = () => {
3296
+ if (this.viewportTimer)
3297
+ clearTimeout(this.viewportTimer);
3298
+ this.viewportTimer = setTimeout(() => {
3299
+ this.viewportTimer = undefined;
3300
+ for (const id of this.frameMaxWidths.keys())
3301
+ this.sizeContentFrame(id);
3302
+ }, 100);
3303
+ };
3304
+ window.addEventListener('resize', this.viewportListener);
3305
+ }
3306
+ unwatchViewportIfIdle() {
3307
+ if (this.frameMaxWidths.size > 0 || !this.viewportListener)
3308
+ return;
3309
+ if (typeof window !== 'undefined' && typeof window.removeEventListener === 'function') {
3310
+ window.removeEventListener('resize', this.viewportListener);
3311
+ }
3312
+ this.viewportListener = undefined;
3313
+ if (this.viewportTimer) {
3314
+ clearTimeout(this.viewportTimer);
3315
+ this.viewportTimer = undefined;
3316
+ }
3317
+ }
3258
3318
  buildSrcdoc(campaignId, html, css, type) {
3259
3319
  const cid = JSON.stringify(campaignId);
3260
3320
  const key = JSON.stringify(HOST_BRIDGE_KEY);
3261
3321
  const contentSized = type === 'modal' || type === 'slideup';
3262
- const bodySizing = contentSized ? 'body{display:inline-block;max-width:100vw;}' : '';
3322
+ const bodySizing = contentSized ? 'body{display:flow-root;}' : '';
3263
3323
  const bootstrap = `(function(){
3264
- var CID=${cid},KEY=${key};
3324
+ var CID=${cid},KEY=${key},SIZED=${contentSized ? 'true' : 'false'};
3265
3325
  // Per-device visibility: tag the root with the REAL device class from the
3266
3326
  // HOST page viewport (the iframe is message-sized, so its own innerWidth is
3267
3327
  // useless here). The jr-vp-* → jr-hide-* rules in <style> then hide elements
@@ -3310,7 +3370,7 @@ class InAppMessagingManager {
3310
3370
  var p=window.joryioBridge.submitForm(d);if(!p||typeof p.then!=='function'){msg('This form cannot be sent here.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false;return}
3311
3371
  p.then(function(r){if(r&&r.ok){if(r.redirect){window.joryioBridge.navigate(r.redirect,'_top');return}var els2=f.querySelectorAll('input,select,textarea,button');for(var j=0;j<els2.length;j++){els2[j].disabled=true}msg(r.message||'Thank you.',true)}else{msg((r&&(r.message||(r.errors&&r.errors[0])))||'Something went wrong. Please try again.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false}}).catch(function(){msg('Something went wrong. Please try again.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false});
3312
3372
  },true);
3313
- function reportSize(){try{var b=document.body,de=document.documentElement;var w=Math.max(b?b.scrollWidth:0,de.scrollWidth);var h=Math.max(b?b.scrollHeight:0,de.scrollHeight);call('setSize',[w,h]);}catch(e){}}
3373
+ function reportSize(){try{var b=document.body,de=document.documentElement;var w=Math.max(b?b.scrollWidth:0,de.scrollWidth);var h=SIZED&&b?Math.ceil(Math.max(b.scrollHeight,b.getBoundingClientRect?b.getBoundingClientRect().height:0)):Math.max(b?b.scrollHeight:0,de.scrollHeight);call('setSize',[w,h]);}catch(e){}}
3314
3374
  if(document.readyState!=='loading')reportSize();else document.addEventListener('DOMContentLoaded',reportSize);
3315
3375
  window.addEventListener('load',reportSize);
3316
3376
  try{if(window.ResizeObserver&&document.body){new ResizeObserver(reportSize).observe(document.body);}}catch(e){}
@@ -3372,6 +3432,8 @@ class InAppMessagingManager {
3372
3432
  container.remove();
3373
3433
  }
3374
3434
  this.iframes.delete(campaignId);
3435
+ this.frameMaxWidths.delete(campaignId);
3436
+ this.unwatchViewportIfIdle();
3375
3437
  if (backdrop) {
3376
3438
  backdrop.remove();
3377
3439
  }
@@ -3599,6 +3661,8 @@ class InAppMessagingManager {
3599
3661
  this.displayedMessages.clear();
3600
3662
  this.watchedAttributes.clear();
3601
3663
  this.iframes.clear();
3664
+ this.frameMaxWidths.clear();
3665
+ this.unwatchViewportIfIdle();
3602
3666
  this.logger.info('In-app messaging state cleared');
3603
3667
  }
3604
3668
  forgetImpressions() {
@@ -3639,6 +3703,8 @@ class InAppMessagingManager {
3639
3703
  this.impressionTimestamps.clear();
3640
3704
  this.watchedAttributes.clear();
3641
3705
  this.iframes.clear();
3706
+ this.frameMaxWidths.clear();
3707
+ this.unwatchViewportIfIdle();
3642
3708
  if (typeof window !== 'undefined') {
3643
3709
  delete window[HOST_BRIDGE_KEY];
3644
3710
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Joryio Web SDK v1.3.0
2
+ * Joryio Web SDK v1.3.1-beta.2
3
3
  * (c) 2026 Joryio. All rights reserved.
4
4
  * @license Commercial - see https://github.com/joryio/joryio-web-sdk/blob/main/LICENSE
5
5
  */
@@ -9,7 +9,7 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JoryioSDK = {}));
10
10
  })(this, (function (exports) { 'use strict';
11
11
 
12
- const SDK_VERSION = '1.3.0';
12
+ const SDK_VERSION = '1.3.1';
13
13
  const DEFAULT_API_ENDPOINT = 'https://api-eu1.joryio.com/api';
14
14
  function normalizeApiEndpoint(raw) {
15
15
  const trimmed = (raw || '').trim();
@@ -2267,6 +2267,21 @@
2267
2267
  function escapeStyleText(css) {
2268
2268
  return typeof css === 'string' ? css.replace(/<\//g, '<\\/') : '';
2269
2269
  }
2270
+ const IN_APP_DEFAULT_MAX_WIDTH = { modal: 600, slideup: 400 };
2271
+ const IN_APP_VIEWPORT_MARGIN = 16;
2272
+ function inAppMaxWidth(campaign) {
2273
+ var _a;
2274
+ if (campaign.type !== 'modal' && campaign.type !== 'slideup')
2275
+ return null;
2276
+ if (((_a = campaign.content) === null || _a === void 0 ? void 0 : _a.kind) !== 'html')
2277
+ return null;
2278
+ const mw = campaign.content.maxWidth;
2279
+ return typeof mw === 'number' && Number.isFinite(mw) && mw > 0 ? Math.floor(mw) : IN_APP_DEFAULT_MAX_WIDTH[campaign.type];
2280
+ }
2281
+ function inAppFrameWidth(maxWidth, viewportWidth, margin = IN_APP_VIEWPORT_MARGIN) {
2282
+ const available = Math.max(0, Math.floor(viewportWidth) - 2 * margin);
2283
+ return Math.max(0, Math.min(Math.floor(maxWidth), available));
2284
+ }
2270
2285
  const HOST_BRIDGE_KEY = '__joryioInApp';
2271
2286
  const IMPRESSIONS_STORAGE_KEY = 'inapp_impressions';
2272
2287
  const MAX_DISPLAYED_MESSAGES = 500;
@@ -2382,6 +2397,7 @@
2382
2397
  this.syncAgain = false;
2383
2398
  this.claimed = new Set();
2384
2399
  this.iframes = new Map();
2400
+ this.frameMaxWidths = new Map();
2385
2401
  this.allowHtmlJsInAppMessages = false;
2386
2402
  this.bridge = null;
2387
2403
  this.auth = null;
@@ -2501,16 +2517,13 @@
2501
2517
  this.trackImpression(campaignId, 'clicked').catch(() => { });
2502
2518
  return this.submitForm(campaignId, values);
2503
2519
  },
2504
- setSize: (campaignId, width, height) => {
2520
+ setSize: (campaignId, _width, height) => {
2505
2521
  var _a;
2506
2522
  const iframe = this.iframes.get(campaignId);
2507
2523
  if (!iframe || !Number.isFinite(height) || height <= 0)
2508
2524
  return;
2509
2525
  const type = (_a = this.displayedMessages.get(campaignId)) === null || _a === void 0 ? void 0 : _a.campaign.type;
2510
2526
  iframe.style.height = type === 'fullscreen' ? '100%' : `${Math.ceil(height)}px`;
2511
- if ((type === 'modal' || type === 'slideup') && Number.isFinite(width) && width > 0) {
2512
- iframe.style.width = `${Math.ceil(width)}px`;
2513
- }
2514
2527
  },
2515
2528
  };
2516
2529
  }
@@ -3208,6 +3221,13 @@
3208
3221
  if (campaign.type === 'banner' || campaign.type === 'fullscreen') {
3209
3222
  iframe.style.width = '100%';
3210
3223
  }
3224
+ const maxWidth = inAppMaxWidth(campaign);
3225
+ if (maxWidth !== null) {
3226
+ container.style.maxWidth = 'none';
3227
+ this.frameMaxWidths.set(campaign.id, maxWidth);
3228
+ iframe.style.width = `${inAppFrameWidth(maxWidth, this.hostViewportWidth())}px`;
3229
+ this.watchViewport();
3230
+ }
3211
3231
  iframe.srcdoc = this.buildSrcdoc(campaign.id, renderedHtml, ((_d = campaign.content) === null || _d === void 0 ? void 0 : _d.kind) === 'html' ? campaign.content.css : '', campaign.type);
3212
3232
  container.appendChild(iframe);
3213
3233
  this.iframes.set(campaign.id, iframe);
@@ -3261,13 +3281,53 @@
3261
3281
  this.callHost('onMessageDisplay', () => { var _a, _b; return (_b = (_a = this.displayOptions).onDisplay) === null || _b === void 0 ? void 0 : _b.call(_a, message); });
3262
3282
  this.logger.info(`Displayed campaign: ${campaign.name} (${campaign.id})`);
3263
3283
  }
3284
+ hostViewportWidth() {
3285
+ const doc = typeof document !== 'undefined' ? document.documentElement : null;
3286
+ const cw = doc && typeof doc.clientWidth === 'number' ? doc.clientWidth : 0;
3287
+ if (cw > 0)
3288
+ return cw;
3289
+ return typeof window !== 'undefined' && typeof window.innerWidth === 'number' ? window.innerWidth : 0;
3290
+ }
3291
+ sizeContentFrame(campaignId) {
3292
+ const iframe = this.iframes.get(campaignId);
3293
+ const maxWidth = this.frameMaxWidths.get(campaignId);
3294
+ if (!iframe || maxWidth === undefined)
3295
+ return;
3296
+ iframe.style.width = `${inAppFrameWidth(maxWidth, this.hostViewportWidth())}px`;
3297
+ }
3298
+ watchViewport() {
3299
+ if (this.viewportListener || typeof window === 'undefined' || typeof window.addEventListener !== 'function')
3300
+ return;
3301
+ this.viewportListener = () => {
3302
+ if (this.viewportTimer)
3303
+ clearTimeout(this.viewportTimer);
3304
+ this.viewportTimer = setTimeout(() => {
3305
+ this.viewportTimer = undefined;
3306
+ for (const id of this.frameMaxWidths.keys())
3307
+ this.sizeContentFrame(id);
3308
+ }, 100);
3309
+ };
3310
+ window.addEventListener('resize', this.viewportListener);
3311
+ }
3312
+ unwatchViewportIfIdle() {
3313
+ if (this.frameMaxWidths.size > 0 || !this.viewportListener)
3314
+ return;
3315
+ if (typeof window !== 'undefined' && typeof window.removeEventListener === 'function') {
3316
+ window.removeEventListener('resize', this.viewportListener);
3317
+ }
3318
+ this.viewportListener = undefined;
3319
+ if (this.viewportTimer) {
3320
+ clearTimeout(this.viewportTimer);
3321
+ this.viewportTimer = undefined;
3322
+ }
3323
+ }
3264
3324
  buildSrcdoc(campaignId, html, css, type) {
3265
3325
  const cid = JSON.stringify(campaignId);
3266
3326
  const key = JSON.stringify(HOST_BRIDGE_KEY);
3267
3327
  const contentSized = type === 'modal' || type === 'slideup';
3268
- const bodySizing = contentSized ? 'body{display:inline-block;max-width:100vw;}' : '';
3328
+ const bodySizing = contentSized ? 'body{display:flow-root;}' : '';
3269
3329
  const bootstrap = `(function(){
3270
- var CID=${cid},KEY=${key};
3330
+ var CID=${cid},KEY=${key},SIZED=${contentSized ? 'true' : 'false'};
3271
3331
  // Per-device visibility: tag the root with the REAL device class from the
3272
3332
  // HOST page viewport (the iframe is message-sized, so its own innerWidth is
3273
3333
  // useless here). The jr-vp-* → jr-hide-* rules in <style> then hide elements
@@ -3316,7 +3376,7 @@
3316
3376
  var p=window.joryioBridge.submitForm(d);if(!p||typeof p.then!=='function'){msg('This form cannot be sent here.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false;return}
3317
3377
  p.then(function(r){if(r&&r.ok){if(r.redirect){window.joryioBridge.navigate(r.redirect,'_top');return}var els2=f.querySelectorAll('input,select,textarea,button');for(var j=0;j<els2.length;j++){els2[j].disabled=true}msg(r.message||'Thank you.',true)}else{msg((r&&(r.message||(r.errors&&r.errors[0])))||'Something went wrong. Please try again.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false}}).catch(function(){msg('Something went wrong. Please try again.',false);f.removeAttribute('data-jry-busy');if(btn)btn.disabled=false});
3318
3378
  },true);
3319
- function reportSize(){try{var b=document.body,de=document.documentElement;var w=Math.max(b?b.scrollWidth:0,de.scrollWidth);var h=Math.max(b?b.scrollHeight:0,de.scrollHeight);call('setSize',[w,h]);}catch(e){}}
3379
+ function reportSize(){try{var b=document.body,de=document.documentElement;var w=Math.max(b?b.scrollWidth:0,de.scrollWidth);var h=SIZED&&b?Math.ceil(Math.max(b.scrollHeight,b.getBoundingClientRect?b.getBoundingClientRect().height:0)):Math.max(b?b.scrollHeight:0,de.scrollHeight);call('setSize',[w,h]);}catch(e){}}
3320
3380
  if(document.readyState!=='loading')reportSize();else document.addEventListener('DOMContentLoaded',reportSize);
3321
3381
  window.addEventListener('load',reportSize);
3322
3382
  try{if(window.ResizeObserver&&document.body){new ResizeObserver(reportSize).observe(document.body);}}catch(e){}
@@ -3378,6 +3438,8 @@
3378
3438
  container.remove();
3379
3439
  }
3380
3440
  this.iframes.delete(campaignId);
3441
+ this.frameMaxWidths.delete(campaignId);
3442
+ this.unwatchViewportIfIdle();
3381
3443
  if (backdrop) {
3382
3444
  backdrop.remove();
3383
3445
  }
@@ -3605,6 +3667,8 @@
3605
3667
  this.displayedMessages.clear();
3606
3668
  this.watchedAttributes.clear();
3607
3669
  this.iframes.clear();
3670
+ this.frameMaxWidths.clear();
3671
+ this.unwatchViewportIfIdle();
3608
3672
  this.logger.info('In-app messaging state cleared');
3609
3673
  }
3610
3674
  forgetImpressions() {
@@ -3645,6 +3709,8 @@
3645
3709
  this.impressionTimestamps.clear();
3646
3710
  this.watchedAttributes.clear();
3647
3711
  this.iframes.clear();
3712
+ this.frameMaxWidths.clear();
3713
+ this.unwatchViewportIfIdle();
3648
3714
  if (typeof window !== 'undefined') {
3649
3715
  delete window[HOST_BRIDGE_KEY];
3650
3716
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joryio/web-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.3.1-beta.2",
4
4
  "description": "Joryio Web Tracking SDK - Lightweight analytics and event tracking",
5
5
  "main": "dist/joryio.cjs.js",
6
6
  "module": "dist/joryio.mjs",
@@ -2,7 +2,7 @@
2
2
  * SDK Configuration Types and Defaults
3
3
  */
4
4
 
5
- export const SDK_VERSION = '1.3.0'; // keep in sync with package.json — the backend serves this in X-SDK-Version headers
5
+ export const SDK_VERSION = '1.3.1'; // keep in sync with package.json — the backend serves this in X-SDK-Version headers
6
6
  export const DEFAULT_API_ENDPOINT = 'https://api-eu1.joryio.com/api';
7
7
 
8
8
  /**
@@ -88,17 +88,20 @@ export interface InAppOptions {
88
88
  *
89
89
  * What actually happens, because the name alone cannot carry it:
90
90
  *
91
- * - The server strips `<script>` and every `on*` handler from the authored
92
- * HTML before it is sent, so author JavaScript does NOT run in normal
93
- * operation.
94
- * - The message is still rendered in an iframe with `allow-scripts
95
- * allow-same-origin`, because this SDK's own bootstrap runs there (height
91
+ * - The server keeps the authored HTML as written (only `<base>` and
92
+ * `<meta http-equiv="refresh">` are removed), so `<script>` and `on*`
93
+ * handlers reach the browser - and do NOT run: the frame carries a fresh
94
+ * nonce Content-Security-Policy per message, and only the SDK's bootstrap
95
+ * has the nonce. Buttons and forms work through that bootstrap
96
+ * (`data-joryio-action`, `data-jry-form`), not through author script.
97
+ * - The message is rendered in an iframe with `allow-scripts
98
+ * allow-same-origin`, because the bootstrap runs there (height
96
99
  * reporting, click logging, the host bridge). That frame therefore CAN
97
100
  * execute script, at your page's origin.
98
- * - So the residual risk is a sanitiser bypass: if one were ever found,
99
- * injected script would run with your origin's privileges. That is why
100
- * this is opt-in and why the name keeps `Js` - `allowHtmlInAppMessages`
101
- * alone would read like a formatting preference.
101
+ * - So the residual risk is a gap in the CSP (a browser that does not
102
+ * enforce it): author script would then run with your origin's
103
+ * privileges. That is why this is opt-in and why the name keeps `Js` -
104
+ * `allowHtmlInAppMessages` alone would read like a formatting preference.
102
105
  *
103
106
  * Leaving it off does not disable in-app messaging: NATIVE messages still
104
107
  * display, because they are data rendered into DOM text nodes with no