@atlaskit/react-ufo 5.0.0 → 5.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/ufo-interaction-ignore
2
2
 
3
+ ## 5.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`a394e2061cccd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a394e2061cccd) -
8
+ classify attribute changes from routing as mutation:attribute:framework-routing in VC observer,
9
+ and exclude it from TTVC v3 onwards
10
+
11
+ ## 5.0.1
12
+
13
+ ### Patch Changes
14
+
15
+ - [`60444262e8606`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/60444262e8606) -
16
+ Deduplicate reported VC offenders in UFO payload
17
+
3
18
  ## 5.0.0
4
19
 
5
20
  ### Major Changes
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
13
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -164,9 +164,18 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
164
164
  case 19:
165
165
  // Check if this entry matches any checkpoint percentiles
166
166
  if (viewportPercentage >= percentiles[percentileIndex]) {
167
- elementNames = entries.map(function (e) {
168
- return e.elementName;
169
- }); // Process all matching percentiles in one go
167
+ elementNames = [];
168
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_dedupe_repeated_vc_offenders')) {
169
+ elementNames = (0, _toConsumableArray2.default)(new Set(entries.map(function (e) {
170
+ return e.elementName;
171
+ })));
172
+ } else {
173
+ elementNames = entries.map(function (e) {
174
+ return e.elementName;
175
+ });
176
+ }
177
+
178
+ // Process all matching percentiles in one go
170
179
  while (percentileIndex < percentiles.length && viewportPercentage >= percentiles[percentileIndex]) {
171
180
  vcDetails["".concat(percentiles[percentileIndex])] = {
172
181
  t: Math.round(time),
@@ -54,6 +54,9 @@ var VCCalculator_FY25_03 = exports.default = /*#__PURE__*/function (_AbstractVCC
54
54
  if (!getConsideredEntryTypes(include3p, excludeSmartAnswersInSearch).includes(entry.data.type)) {
55
55
  return false;
56
56
  }
57
+ if (entry.data.type === 'mutation:attribute:framework-routing' && (0, _platformFeatureFlags.fg)('platform_ufo_vc_ignore_display_none_mutations')) {
58
+ return false;
59
+ }
57
60
  if (excludeSmartAnswersInSearch && (0, _isEntrySmartAnswersInSearch.isEntrySmartAnswersInSearch)(entry) && !(0, _platformFeatureFlags.fg)('rovo_search_page_ttvc_ignoring_smart_answers_fix')) {
58
61
  return false;
59
62
  }
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.createIntersectionObserver = createIntersectionObserver;
7
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
7
8
  var _isZeroDimensionRectangle = require("../utils/is-zero-dimension-rectangle");
8
9
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
9
10
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -37,10 +38,28 @@ function createIntersectionObserver(_ref) {
37
38
 
38
39
  // override as display-contents mutation
39
40
  if (tagOrCallbackResult && typeof tagOrCallbackResult !== 'string' && tagOrCallbackResult.type === 'mutation:attribute') {
40
- return {
41
- type: 'mutation:display-contents-children-attribute',
42
- mutationData: tagOrCallbackResult.mutationData
43
- };
41
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vc_ignore_display_none_mutations')) {
42
+ var _tagOrCallbackResult$ = tagOrCallbackResult.mutationData,
43
+ attributeName = _tagOrCallbackResult$.attributeName,
44
+ oldValue = _tagOrCallbackResult$.oldValue,
45
+ newValue = _tagOrCallbackResult$.newValue;
46
+ var isRoutingMutation = attributeName === 'style' && (!oldValue && newValue === 'display: none !important;' || oldValue === 'display: none !important;' && !newValue);
47
+ if (isRoutingMutation) {
48
+ return {
49
+ type: 'mutation:attribute:framework-routing',
50
+ mutationData: tagOrCallbackResult.mutationData
51
+ };
52
+ }
53
+ return {
54
+ type: 'mutation:display-contents-children-attribute',
55
+ mutationData: tagOrCallbackResult.mutationData
56
+ };
57
+ } else {
58
+ return {
59
+ type: 'mutation:display-contents-children-attribute',
60
+ mutationData: tagOrCallbackResult.mutationData
61
+ };
62
+ }
44
63
  }
45
64
  return tagOrCallbackResult;
46
65
  };
@@ -43,13 +43,13 @@ function createMutationObserver(_ref) {
43
43
  if (mut.type === 'attributes') {
44
44
  var _mut$oldValue;
45
45
  /*
46
- "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
47
- I think it might have been something along the lines that for consistency every setAttribute call should create a record.
48
- Conceptually there is after all a mutation: there is an old value replaced with a new one,
49
- and whether or not they are the same doesn't really matter.
50
- And Custom elements should work the same way as MutationObserver."
51
- https://github.com/whatwg/dom/issues/520#issuecomment-336574796
52
- */
46
+ "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
47
+ I think it might have been something along the lines that for consistency every setAttribute call should create a record.
48
+ Conceptually there is after all a mutation: there is an old value replaced with a new one,
49
+ and whether or not they are the same doesn't really matter.
50
+ And Custom elements should work the same way as MutationObserver."
51
+ https://github.com/whatwg/dom/issues/520#issuecomment-336574796
52
+ */
53
53
  var oldValue = (_mut$oldValue = mut.oldValue) !== null && _mut$oldValue !== void 0 ? _mut$oldValue : undefined;
54
54
  var newValue = mut.attributeName ? mut.target.getAttribute(mut.attributeName) : undefined;
55
55
  if (oldValue !== newValue) {
@@ -90,7 +90,12 @@ export default class AbstractVCCalculatorBase {
90
90
 
91
91
  // Check if this entry matches any checkpoint percentiles
92
92
  if (viewportPercentage >= percentiles[percentileIndex]) {
93
- const elementNames = entries.map(e => e.elementName);
93
+ let elementNames = [];
94
+ if (fg('platform_ufo_dedupe_repeated_vc_offenders')) {
95
+ elementNames = [...new Set(entries.map(e => e.elementName))];
96
+ } else {
97
+ elementNames = entries.map(e => e.elementName);
98
+ }
94
99
 
95
100
  // Process all matching percentiles in one go
96
101
  while (percentileIndex < percentiles.length && viewportPercentage >= percentiles[percentileIndex]) {
@@ -36,6 +36,9 @@ export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
36
36
  if (!getConsideredEntryTypes(include3p, excludeSmartAnswersInSearch).includes(entry.data.type)) {
37
37
  return false;
38
38
  }
39
+ if (entry.data.type === 'mutation:attribute:framework-routing' && fg('platform_ufo_vc_ignore_display_none_mutations')) {
40
+ return false;
41
+ }
39
42
  if (excludeSmartAnswersInSearch && isEntrySmartAnswersInSearch(entry) && !fg('rovo_search_page_ttvc_ignoring_smart_answers_fix')) {
40
43
  return false;
41
44
  }
@@ -1,3 +1,4 @@
1
+ import { fg } from '@atlaskit/platform-feature-flags';
1
2
  import { isZeroDimensionRectangle } from '../utils/is-zero-dimension-rectangle';
2
3
  function isValidEntry(entry) {
3
4
  return entry.isIntersecting && entry.intersectionRect.width > 0 && entry.intersectionRect.height > 0;
@@ -30,10 +31,29 @@ export function createIntersectionObserver({
30
31
 
31
32
  // override as display-contents mutation
32
33
  if (tagOrCallbackResult && typeof tagOrCallbackResult !== 'string' && tagOrCallbackResult.type === 'mutation:attribute') {
33
- return {
34
- type: 'mutation:display-contents-children-attribute',
35
- mutationData: tagOrCallbackResult.mutationData
36
- };
34
+ if (fg('platform_ufo_vc_ignore_display_none_mutations')) {
35
+ const {
36
+ attributeName,
37
+ oldValue,
38
+ newValue
39
+ } = tagOrCallbackResult.mutationData;
40
+ const isRoutingMutation = attributeName === 'style' && (!oldValue && newValue === 'display: none !important;' || oldValue === 'display: none !important;' && !newValue);
41
+ if (isRoutingMutation) {
42
+ return {
43
+ type: 'mutation:attribute:framework-routing',
44
+ mutationData: tagOrCallbackResult.mutationData
45
+ };
46
+ }
47
+ return {
48
+ type: 'mutation:display-contents-children-attribute',
49
+ mutationData: tagOrCallbackResult.mutationData
50
+ };
51
+ } else {
52
+ return {
53
+ type: 'mutation:display-contents-children-attribute',
54
+ mutationData: tagOrCallbackResult.mutationData
55
+ };
56
+ }
37
57
  }
38
58
  return tagOrCallbackResult;
39
59
  };
@@ -30,13 +30,13 @@ function createMutationObserver({
30
30
  if (mut.type === 'attributes') {
31
31
  var _mut$oldValue;
32
32
  /*
33
- "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
34
- I think it might have been something along the lines that for consistency every setAttribute call should create a record.
35
- Conceptually there is after all a mutation: there is an old value replaced with a new one,
36
- and whether or not they are the same doesn't really matter.
37
- And Custom elements should work the same way as MutationObserver."
38
- https://github.com/whatwg/dom/issues/520#issuecomment-336574796
39
- */
33
+ "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
34
+ I think it might have been something along the lines that for consistency every setAttribute call should create a record.
35
+ Conceptually there is after all a mutation: there is an old value replaced with a new one,
36
+ and whether or not they are the same doesn't really matter.
37
+ And Custom elements should work the same way as MutationObserver."
38
+ https://github.com/whatwg/dom/issues/520#issuecomment-336574796
39
+ */
40
40
  const oldValue = (_mut$oldValue = mut.oldValue) !== null && _mut$oldValue !== void 0 ? _mut$oldValue : undefined;
41
41
  const newValue = mut.attributeName ? mut.target.getAttribute(mut.attributeName) : undefined;
42
42
  if (oldValue !== newValue) {
@@ -1,5 +1,5 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
3
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
4
4
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
5
5
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
@@ -158,9 +158,18 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
158
158
  case 19:
159
159
  // Check if this entry matches any checkpoint percentiles
160
160
  if (viewportPercentage >= percentiles[percentileIndex]) {
161
- elementNames = entries.map(function (e) {
162
- return e.elementName;
163
- }); // Process all matching percentiles in one go
161
+ elementNames = [];
162
+ if (fg('platform_ufo_dedupe_repeated_vc_offenders')) {
163
+ elementNames = _toConsumableArray(new Set(entries.map(function (e) {
164
+ return e.elementName;
165
+ })));
166
+ } else {
167
+ elementNames = entries.map(function (e) {
168
+ return e.elementName;
169
+ });
170
+ }
171
+
172
+ // Process all matching percentiles in one go
164
173
  while (percentileIndex < percentiles.length && viewportPercentage >= percentiles[percentileIndex]) {
165
174
  vcDetails["".concat(percentiles[percentileIndex])] = {
166
175
  t: Math.round(time),
@@ -47,6 +47,9 @@ var VCCalculator_FY25_03 = /*#__PURE__*/function (_AbstractVCCalculator) {
47
47
  if (!getConsideredEntryTypes(include3p, excludeSmartAnswersInSearch).includes(entry.data.type)) {
48
48
  return false;
49
49
  }
50
+ if (entry.data.type === 'mutation:attribute:framework-routing' && fg('platform_ufo_vc_ignore_display_none_mutations')) {
51
+ return false;
52
+ }
50
53
  if (excludeSmartAnswersInSearch && isEntrySmartAnswersInSearch(entry) && !fg('rovo_search_page_ttvc_ignoring_smart_answers_fix')) {
51
54
  return false;
52
55
  }
@@ -1,6 +1,7 @@
1
1
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
2
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
3
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
+ import { fg } from '@atlaskit/platform-feature-flags';
4
5
  import { isZeroDimensionRectangle } from '../utils/is-zero-dimension-rectangle';
5
6
  function isValidEntry(entry) {
6
7
  return entry.isIntersecting && entry.intersectionRect.width > 0 && entry.intersectionRect.height > 0;
@@ -31,10 +32,28 @@ export function createIntersectionObserver(_ref) {
31
32
 
32
33
  // override as display-contents mutation
33
34
  if (tagOrCallbackResult && typeof tagOrCallbackResult !== 'string' && tagOrCallbackResult.type === 'mutation:attribute') {
34
- return {
35
- type: 'mutation:display-contents-children-attribute',
36
- mutationData: tagOrCallbackResult.mutationData
37
- };
35
+ if (fg('platform_ufo_vc_ignore_display_none_mutations')) {
36
+ var _tagOrCallbackResult$ = tagOrCallbackResult.mutationData,
37
+ attributeName = _tagOrCallbackResult$.attributeName,
38
+ oldValue = _tagOrCallbackResult$.oldValue,
39
+ newValue = _tagOrCallbackResult$.newValue;
40
+ var isRoutingMutation = attributeName === 'style' && (!oldValue && newValue === 'display: none !important;' || oldValue === 'display: none !important;' && !newValue);
41
+ if (isRoutingMutation) {
42
+ return {
43
+ type: 'mutation:attribute:framework-routing',
44
+ mutationData: tagOrCallbackResult.mutationData
45
+ };
46
+ }
47
+ return {
48
+ type: 'mutation:display-contents-children-attribute',
49
+ mutationData: tagOrCallbackResult.mutationData
50
+ };
51
+ } else {
52
+ return {
53
+ type: 'mutation:display-contents-children-attribute',
54
+ mutationData: tagOrCallbackResult.mutationData
55
+ };
56
+ }
38
57
  }
39
58
  return tagOrCallbackResult;
40
59
  };
@@ -38,13 +38,13 @@ function createMutationObserver(_ref) {
38
38
  if (mut.type === 'attributes') {
39
39
  var _mut$oldValue;
40
40
  /*
41
- "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
42
- I think it might have been something along the lines that for consistency every setAttribute call should create a record.
43
- Conceptually there is after all a mutation: there is an old value replaced with a new one,
44
- and whether or not they are the same doesn't really matter.
45
- And Custom elements should work the same way as MutationObserver."
46
- https://github.com/whatwg/dom/issues/520#issuecomment-336574796
47
- */
41
+ "MutationObserver was explicitly designed to work that way, but I can't now recall the reasoning.
42
+ I think it might have been something along the lines that for consistency every setAttribute call should create a record.
43
+ Conceptually there is after all a mutation: there is an old value replaced with a new one,
44
+ and whether or not they are the same doesn't really matter.
45
+ And Custom elements should work the same way as MutationObserver."
46
+ https://github.com/whatwg/dom/issues/520#issuecomment-336574796
47
+ */
48
48
  var oldValue = (_mut$oldValue = mut.oldValue) !== null && _mut$oldValue !== void 0 ? _mut$oldValue : undefined;
49
49
  var newValue = mut.attributeName ? mut.target.getAttribute(mut.attributeName) : undefined;
50
50
  if (oldValue !== newValue) {
@@ -1,6 +1,6 @@
1
1
  import type { AbortReasonType, InteractionType } from '../../common/common/types';
2
2
  import type { ObservedWindowEvent } from './window-event-observer';
3
- export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:display-contents-children-element' | 'mutation:display-contents-children-attribute' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute:non-visual-input-name' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'mutation:third-party-element' | 'mutation:third-party-attribute' | 'mutation:smart-answers-element' | 'mutation:smart-answers-attribute' | 'mutation:ssr-placeholder' | 'layout-shift' | 'layout-shift:same-rect' | 'window:event' | 'ssr-hydration' | 'unknown';
3
+ export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:display-contents-children-element' | 'mutation:display-contents-children-attribute' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute:non-visual-input-name' | 'mutation:attribute:framework-routing' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'mutation:third-party-element' | 'mutation:third-party-attribute' | 'mutation:smart-answers-element' | 'mutation:smart-answers-attribute' | 'mutation:ssr-placeholder' | 'layout-shift' | 'layout-shift:same-rect' | 'window:event' | 'ssr-hydration' | 'unknown';
4
4
  export type ViewportEntryData = {
5
5
  readonly type: VCObserverEntryType;
6
6
  readonly elementName: string;
@@ -1,6 +1,6 @@
1
1
  import type { AbortReasonType, InteractionType } from '../../common/common/types';
2
2
  import type { ObservedWindowEvent } from './window-event-observer';
3
- export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:display-contents-children-element' | 'mutation:display-contents-children-attribute' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute:non-visual-input-name' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'mutation:third-party-element' | 'mutation:third-party-attribute' | 'mutation:smart-answers-element' | 'mutation:smart-answers-attribute' | 'mutation:ssr-placeholder' | 'layout-shift' | 'layout-shift:same-rect' | 'window:event' | 'ssr-hydration' | 'unknown';
3
+ export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:display-contents-children-element' | 'mutation:display-contents-children-attribute' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute:non-visual-input-name' | 'mutation:attribute:framework-routing' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'mutation:third-party-element' | 'mutation:third-party-attribute' | 'mutation:smart-answers-element' | 'mutation:smart-answers-attribute' | 'mutation:ssr-placeholder' | 'layout-shift' | 'layout-shift:same-rect' | 'window:event' | 'ssr-hydration' | 'unknown';
4
4
  export type ViewportEntryData = {
5
5
  readonly type: VCObserverEntryType;
6
6
  readonly elementName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/react-ufo",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Parts of React UFO that are publicly available",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -192,9 +192,15 @@
192
192
  "platform_ufo_enable_vc_raw_data": {
193
193
  "type": "boolean"
194
194
  },
195
+ "platform_ufo_dedupe_repeated_vc_offenders": {
196
+ "type": "boolean"
197
+ },
195
198
  "platform_mark_ufo_segment_first_load": {
196
199
  "type": "boolean"
197
200
  },
201
+ "platform_ufo_vc_ignore_display_none_mutations": {
202
+ "type": "boolean"
203
+ },
198
204
  "platform_ufo_keypress_interaction_abort": {
199
205
  "type": "boolean"
200
206
  },