wicg-inert 3.0.1 → 3.1.1

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/src/inert.js CHANGED
@@ -3,730 +3,737 @@
3
3
  * (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
4
4
  */
5
5
 
6
- // Convenience function for converting NodeLists.
7
- /** @type {typeof Array.prototype.slice} */
8
- const slice = Array.prototype.slice;
6
+ (function() {
7
+ // Return early if we're not running inside of the browser.
8
+ if (typeof window === 'undefined') {
9
+ return;
10
+ }
9
11
 
10
- /**
11
- * IE has a non-standard name for "matches".
12
- * @type {typeof Element.prototype.matches}
13
- */
14
- const matches =
15
- Element.prototype.matches || Element.prototype.msMatchesSelector;
16
-
17
- /** @type {string} */
18
- const _focusableElementsString = ['a[href]',
19
- 'area[href]',
20
- 'input:not([disabled])',
21
- 'select:not([disabled])',
22
- 'textarea:not([disabled])',
23
- 'button:not([disabled])',
24
- 'details',
25
- 'summary',
26
- 'iframe',
27
- 'object',
28
- 'embed',
29
- '[contenteditable]'].join(',');
12
+ // Convenience function for converting NodeLists.
13
+ /** @type {typeof Array.prototype.slice} */
14
+ const slice = Array.prototype.slice;
30
15
 
31
- /**
32
- * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
33
- * attribute.
34
- *
35
- * Its main functions are:
36
- *
37
- * - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
38
- * subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
39
- * each focusable node in the subtree with the singleton `InertManager` which manages all known
40
- * focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
41
- * instance exists for each focusable node which has at least one inert root as an ancestor.
42
- *
43
- * - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
44
- * attribute is removed from the root node). This is handled in the destructor, which calls the
45
- * `deregister` method on `InertManager` for each managed inert node.
46
- */
47
- class InertRoot {
48
16
  /**
49
- * @param {!Element} rootElement The Element at the root of the inert subtree.
50
- * @param {!InertManager} inertManager The global singleton InertManager object.
17
+ * IE has a non-standard name for "matches".
18
+ * @type {typeof Element.prototype.matches}
51
19
  */
52
- constructor(rootElement, inertManager) {
53
- /** @type {!InertManager} */
54
- this._inertManager = inertManager;
55
-
56
- /** @type {!Element} */
57
- this._rootElement = rootElement;
58
-
59
- /**
60
- * @type {!Set<!InertNode>}
61
- * All managed focusable nodes in this InertRoot's subtree.
62
- */
63
- this._managedNodes = new Set();
64
-
65
- // Make the subtree hidden from assistive technology
66
- if (this._rootElement.hasAttribute('aria-hidden')) {
67
- /** @type {?string} */
68
- this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
69
- } else {
70
- this._savedAriaHidden = null;
71
- }
72
- this._rootElement.setAttribute('aria-hidden', 'true');
73
-
74
- // Make all focusable elements in the subtree unfocusable and add them to _managedNodes
75
- this._makeSubtreeUnfocusable(this._rootElement);
76
-
77
- // Watch for:
78
- // - any additions in the subtree: make them unfocusable too
79
- // - any removals from the subtree: remove them from this inert root's managed nodes
80
- // - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
81
- // element, make that node a managed node.
82
- this._observer = new MutationObserver(this._onMutation.bind(this));
83
- this._observer.observe(this._rootElement, {attributes: true, childList: true, subtree: true});
84
- }
20
+ const matches =
21
+ Element.prototype.matches || Element.prototype.msMatchesSelector;
22
+
23
+ /** @type {string} */
24
+ const _focusableElementsString = ['a[href]',
25
+ 'area[href]',
26
+ 'input:not([disabled])',
27
+ 'select:not([disabled])',
28
+ 'textarea:not([disabled])',
29
+ 'button:not([disabled])',
30
+ 'details',
31
+ 'summary',
32
+ 'iframe',
33
+ 'object',
34
+ 'embed',
35
+ '[contenteditable]'].join(',');
85
36
 
86
37
  /**
87
- * Call this whenever this object is about to become obsolete. This unwinds all of the state
88
- * stored in this object and updates the state of all of the managed nodes.
38
+ * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
39
+ * attribute.
40
+ *
41
+ * Its main functions are:
42
+ *
43
+ * - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
44
+ * subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
45
+ * each focusable node in the subtree with the singleton `InertManager` which manages all known
46
+ * focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
47
+ * instance exists for each focusable node which has at least one inert root as an ancestor.
48
+ *
49
+ * - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
50
+ * attribute is removed from the root node). This is handled in the destructor, which calls the
51
+ * `deregister` method on `InertManager` for each managed inert node.
89
52
  */
90
- destructor() {
91
- this._observer.disconnect();
92
-
93
- if (this._rootElement) {
94
- if (this._savedAriaHidden !== null) {
95
- this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
53
+ class InertRoot {
54
+ /**
55
+ * @param {!Element} rootElement The Element at the root of the inert subtree.
56
+ * @param {!InertManager} inertManager The global singleton InertManager object.
57
+ */
58
+ constructor(rootElement, inertManager) {
59
+ /** @type {!InertManager} */
60
+ this._inertManager = inertManager;
61
+
62
+ /** @type {!Element} */
63
+ this._rootElement = rootElement;
64
+
65
+ /**
66
+ * @type {!Set<!InertNode>}
67
+ * All managed focusable nodes in this InertRoot's subtree.
68
+ */
69
+ this._managedNodes = new Set();
70
+
71
+ // Make the subtree hidden from assistive technology
72
+ if (this._rootElement.hasAttribute('aria-hidden')) {
73
+ /** @type {?string} */
74
+ this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
96
75
  } else {
97
- this._rootElement.removeAttribute('aria-hidden');
76
+ this._savedAriaHidden = null;
98
77
  }
78
+ this._rootElement.setAttribute('aria-hidden', 'true');
79
+
80
+ // Make all focusable elements in the subtree unfocusable and add them to _managedNodes
81
+ this._makeSubtreeUnfocusable(this._rootElement);
82
+
83
+ // Watch for:
84
+ // - any additions in the subtree: make them unfocusable too
85
+ // - any removals from the subtree: remove them from this inert root's managed nodes
86
+ // - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
87
+ // element, make that node a managed node.
88
+ this._observer = new MutationObserver(this._onMutation.bind(this));
89
+ this._observer.observe(this._rootElement, {attributes: true, childList: true, subtree: true});
99
90
  }
100
91
 
101
- this._managedNodes.forEach(function(inertNode) {
102
- this._unmanageNode(inertNode.node);
103
- }, this);
104
-
105
- // Note we cast the nulls to the ANY type here because:
106
- // 1) We want the class properties to be declared as non-null, or else we
107
- // need even more casts throughout this code. All bets are off if an
108
- // instance has been destroyed and a method is called.
109
- // 2) We don't want to cast "this", because we want type-aware optimizations
110
- // to know which properties we're setting.
111
- this._observer = /** @type {?} */ (null);
112
- this._rootElement = /** @type {?} */ (null);
113
- this._managedNodes = /** @type {?} */ (null);
114
- this._inertManager = /** @type {?} */ (null);
115
- }
92
+ /**
93
+ * Call this whenever this object is about to become obsolete. This unwinds all of the state
94
+ * stored in this object and updates the state of all of the managed nodes.
95
+ */
96
+ destructor() {
97
+ this._observer.disconnect();
98
+
99
+ if (this._rootElement) {
100
+ if (this._savedAriaHidden !== null) {
101
+ this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
102
+ } else {
103
+ this._rootElement.removeAttribute('aria-hidden');
104
+ }
105
+ }
116
106
 
117
- /**
118
- * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
119
- */
120
- get managedNodes() {
121
- return new Set(this._managedNodes);
122
- }
107
+ this._managedNodes.forEach(function(inertNode) {
108
+ this._unmanageNode(inertNode.node);
109
+ }, this);
110
+
111
+ // Note we cast the nulls to the ANY type here because:
112
+ // 1) We want the class properties to be declared as non-null, or else we
113
+ // need even more casts throughout this code. All bets are off if an
114
+ // instance has been destroyed and a method is called.
115
+ // 2) We don't want to cast "this", because we want type-aware optimizations
116
+ // to know which properties we're setting.
117
+ this._observer = /** @type {?} */ (null);
118
+ this._rootElement = /** @type {?} */ (null);
119
+ this._managedNodes = /** @type {?} */ (null);
120
+ this._inertManager = /** @type {?} */ (null);
121
+ }
123
122
 
124
- /** @return {boolean} */
125
- get hasSavedAriaHidden() {
126
- return this._savedAriaHidden !== null;
127
- }
123
+ /**
124
+ * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
125
+ */
126
+ get managedNodes() {
127
+ return new Set(this._managedNodes);
128
+ }
128
129
 
129
- /** @param {?string} ariaHidden */
130
- set savedAriaHidden(ariaHidden) {
131
- this._savedAriaHidden = ariaHidden;
132
- }
130
+ /** @return {boolean} */
131
+ get hasSavedAriaHidden() {
132
+ return this._savedAriaHidden !== null;
133
+ }
133
134
 
134
- /** @return {?string} */
135
- get savedAriaHidden() {
136
- return this._savedAriaHidden;
137
- }
135
+ /** @param {?string} ariaHidden */
136
+ set savedAriaHidden(ariaHidden) {
137
+ this._savedAriaHidden = ariaHidden;
138
+ }
138
139
 
139
- /**
140
- * @param {!Node} startNode
141
- */
142
- _makeSubtreeUnfocusable(startNode) {
143
- composedTreeWalk(startNode, (node) => this._visitNode(node));
144
-
145
- let activeElement = document.activeElement;
146
-
147
- if (!document.body.contains(startNode)) {
148
- // startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
149
- let node = startNode;
150
- /** @type {!ShadowRoot|undefined} */
151
- let root = undefined;
152
- while (node) {
153
- if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
154
- root = /** @type {!ShadowRoot} */ (node);
155
- break;
140
+ /** @return {?string} */
141
+ get savedAriaHidden() {
142
+ return this._savedAriaHidden;
143
+ }
144
+
145
+ /**
146
+ * @param {!Node} startNode
147
+ */
148
+ _makeSubtreeUnfocusable(startNode) {
149
+ composedTreeWalk(startNode, (node) => this._visitNode(node));
150
+
151
+ let activeElement = document.activeElement;
152
+
153
+ if (!document.body.contains(startNode)) {
154
+ // startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
155
+ let node = startNode;
156
+ /** @type {!ShadowRoot|undefined} */
157
+ let root = undefined;
158
+ while (node) {
159
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
160
+ root = /** @type {!ShadowRoot} */ (node);
161
+ break;
162
+ }
163
+ node = node.parentNode;
164
+ }
165
+ if (root) {
166
+ activeElement = root.activeElement;
156
167
  }
157
- node = node.parentNode;
158
168
  }
159
- if (root) {
160
- activeElement = root.activeElement;
169
+ if (startNode.contains(activeElement)) {
170
+ activeElement.blur();
171
+ // In IE11, if an element is already focused, and then set to tabindex=-1
172
+ // calling blur() will not actually move the focus.
173
+ // To work around this we call focus() on the body instead.
174
+ if (activeElement === document.activeElement) {
175
+ document.body.focus();
176
+ }
161
177
  }
162
178
  }
163
- if (startNode.contains(activeElement)) {
164
- activeElement.blur();
165
- // In IE11, if an element is already focused, and then set to tabindex=-1
166
- // calling blur() will not actually move the focus.
167
- // To work around this we call focus() on the body instead.
168
- if (activeElement === document.activeElement) {
169
- document.body.focus();
179
+
180
+ /**
181
+ * @param {!Node} node
182
+ */
183
+ _visitNode(node) {
184
+ if (node.nodeType !== Node.ELEMENT_NODE) {
185
+ return;
170
186
  }
171
- }
172
- }
187
+ const element = /** @type {!Element} */ (node);
173
188
 
174
- /**
175
- * @param {!Node} node
176
- */
177
- _visitNode(node) {
178
- if (node.nodeType !== Node.ELEMENT_NODE) {
179
- return;
180
- }
181
- const element = /** @type {!Element} */ (node);
189
+ // If a descendant inert root becomes un-inert, its descendants will still be inert because of
190
+ // this inert root, so all of its managed nodes need to be adopted by this InertRoot.
191
+ if (element !== this._rootElement && element.hasAttribute('inert')) {
192
+ this._adoptInertRoot(element);
193
+ }
182
194
 
183
- // If a descendant inert root becomes un-inert, its descendants will still be inert because of
184
- // this inert root, so all of its managed nodes need to be adopted by this InertRoot.
185
- if (element !== this._rootElement && element.hasAttribute('inert')) {
186
- this._adoptInertRoot(element);
195
+ if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
196
+ this._manageNode(element);
197
+ }
187
198
  }
188
199
 
189
- if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
190
- this._manageNode(element);
200
+ /**
201
+ * Register the given node with this InertRoot and with InertManager.
202
+ * @param {!Node} node
203
+ */
204
+ _manageNode(node) {
205
+ const inertNode = this._inertManager.register(node, this);
206
+ this._managedNodes.add(inertNode);
191
207
  }
192
- }
193
-
194
- /**
195
- * Register the given node with this InertRoot and with InertManager.
196
- * @param {!Node} node
197
- */
198
- _manageNode(node) {
199
- const inertNode = this._inertManager.register(node, this);
200
- this._managedNodes.add(inertNode);
201
- }
202
208
 
203
- /**
204
- * Unregister the given node with this InertRoot and with InertManager.
205
- * @param {!Node} node
206
- */
207
- _unmanageNode(node) {
208
- const inertNode = this._inertManager.deregister(node, this);
209
- if (inertNode) {
210
- this._managedNodes.delete(inertNode);
209
+ /**
210
+ * Unregister the given node with this InertRoot and with InertManager.
211
+ * @param {!Node} node
212
+ */
213
+ _unmanageNode(node) {
214
+ const inertNode = this._inertManager.deregister(node, this);
215
+ if (inertNode) {
216
+ this._managedNodes.delete(inertNode);
217
+ }
211
218
  }
212
- }
213
219
 
214
- /**
215
- * Unregister the entire subtree starting at `startNode`.
216
- * @param {!Node} startNode
217
- */
218
- _unmanageSubtree(startNode) {
219
- composedTreeWalk(startNode, (node) => this._unmanageNode(node));
220
- }
220
+ /**
221
+ * Unregister the entire subtree starting at `startNode`.
222
+ * @param {!Node} startNode
223
+ */
224
+ _unmanageSubtree(startNode) {
225
+ composedTreeWalk(startNode, (node) => this._unmanageNode(node));
226
+ }
221
227
 
222
- /**
223
- * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
224
- * @param {!Element} node
225
- */
226
- _adoptInertRoot(node) {
227
- let inertSubroot = this._inertManager.getInertRoot(node);
228
+ /**
229
+ * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
230
+ * @param {!Element} node
231
+ */
232
+ _adoptInertRoot(node) {
233
+ let inertSubroot = this._inertManager.getInertRoot(node);
234
+
235
+ // During initialisation this inert root may not have been registered yet,
236
+ // so register it now if need be.
237
+ if (!inertSubroot) {
238
+ this._inertManager.setInert(node, true);
239
+ inertSubroot = this._inertManager.getInertRoot(node);
240
+ }
228
241
 
229
- // During initialisation this inert root may not have been registered yet,
230
- // so register it now if need be.
231
- if (!inertSubroot) {
232
- this._inertManager.setInert(node, true);
233
- inertSubroot = this._inertManager.getInertRoot(node);
242
+ inertSubroot.managedNodes.forEach(function(savedInertNode) {
243
+ this._manageNode(savedInertNode.node);
244
+ }, this);
234
245
  }
235
246
 
236
- inertSubroot.managedNodes.forEach(function(savedInertNode) {
237
- this._manageNode(savedInertNode.node);
238
- }, this);
239
- }
240
-
241
- /**
242
- * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
243
- * @param {!Array<!MutationRecord>} records
244
- * @param {!MutationObserver} self
245
- */
246
- _onMutation(records, self) {
247
- records.forEach(function(record) {
248
- const target = /** @type {!Element} */ (record.target);
249
- if (record.type === 'childList') {
250
- // Manage added nodes
251
- slice.call(record.addedNodes).forEach(function(node) {
252
- this._makeSubtreeUnfocusable(node);
253
- }, this);
254
-
255
- // Un-manage removed nodes
256
- slice.call(record.removedNodes).forEach(function(node) {
257
- this._unmanageSubtree(node);
258
- }, this);
259
- } else if (record.type === 'attributes') {
260
- if (record.attributeName === 'tabindex') {
261
- // Re-initialise inert node if tabindex changes
262
- this._manageNode(target);
263
- } else if (target !== this._rootElement &&
264
- record.attributeName === 'inert' &&
265
- target.hasAttribute('inert')) {
266
- // If a new inert root is added, adopt its managed nodes and make sure it knows about the
267
- // already managed nodes from this inert subroot.
268
- this._adoptInertRoot(target);
269
- const inertSubroot = this._inertManager.getInertRoot(target);
270
- this._managedNodes.forEach(function(managedNode) {
271
- if (target.contains(managedNode.node)) {
272
- inertSubroot._manageNode(managedNode.node);
273
- }
274
- });
247
+ /**
248
+ * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
249
+ * @param {!Array<!MutationRecord>} records
250
+ * @param {!MutationObserver} self
251
+ */
252
+ _onMutation(records, self) {
253
+ records.forEach(function(record) {
254
+ const target = /** @type {!Element} */ (record.target);
255
+ if (record.type === 'childList') {
256
+ // Manage added nodes
257
+ slice.call(record.addedNodes).forEach(function(node) {
258
+ this._makeSubtreeUnfocusable(node);
259
+ }, this);
260
+
261
+ // Un-manage removed nodes
262
+ slice.call(record.removedNodes).forEach(function(node) {
263
+ this._unmanageSubtree(node);
264
+ }, this);
265
+ } else if (record.type === 'attributes') {
266
+ if (record.attributeName === 'tabindex') {
267
+ // Re-initialise inert node if tabindex changes
268
+ this._manageNode(target);
269
+ } else if (target !== this._rootElement &&
270
+ record.attributeName === 'inert' &&
271
+ target.hasAttribute('inert')) {
272
+ // If a new inert root is added, adopt its managed nodes and make sure it knows about the
273
+ // already managed nodes from this inert subroot.
274
+ this._adoptInertRoot(target);
275
+ const inertSubroot = this._inertManager.getInertRoot(target);
276
+ this._managedNodes.forEach(function(managedNode) {
277
+ if (target.contains(managedNode.node)) {
278
+ inertSubroot._manageNode(managedNode.node);
279
+ }
280
+ });
281
+ }
275
282
  }
276
- }
277
- }, this);
283
+ }, this);
284
+ }
278
285
  }
279
- }
280
286
 
281
- /**
282
- * `InertNode` initialises and manages a single inert node.
283
- * A node is inert if it is a descendant of one or more inert root elements.
284
- *
285
- * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
286
- * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
287
- * is intrinsically focusable or not.
288
- *
289
- * `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
290
- * `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
291
- * `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
292
- * remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
293
- * or removes the `tabindex` attribute if the element is intrinsically focusable.
294
- */
295
- class InertNode {
296
287
  /**
297
- * @param {!Node} node A focusable element to be made inert.
298
- * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
288
+ * `InertNode` initialises and manages a single inert node.
289
+ * A node is inert if it is a descendant of one or more inert root elements.
290
+ *
291
+ * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
292
+ * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
293
+ * is intrinsically focusable or not.
294
+ *
295
+ * `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
296
+ * `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
297
+ * `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
298
+ * remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
299
+ * or removes the `tabindex` attribute if the element is intrinsically focusable.
299
300
  */
300
- constructor(node, inertRoot) {
301
- /** @type {!Node} */
302
- this._node = node;
303
-
304
- /** @type {boolean} */
305
- this._overrodeFocusMethod = false;
306
-
301
+ class InertNode {
307
302
  /**
308
- * @type {!Set<!InertRoot>} The set of descendant inert roots.
309
- * If and only if this set becomes empty, this node is no longer inert.
303
+ * @param {!Node} node A focusable element to be made inert.
304
+ * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
310
305
  */
311
- this._inertRoots = new Set([inertRoot]);
306
+ constructor(node, inertRoot) {
307
+ /** @type {!Node} */
308
+ this._node = node;
312
309
 
313
- /** @type {?number} */
314
- this._savedTabIndex = null;
310
+ /** @type {boolean} */
311
+ this._overrodeFocusMethod = false;
315
312
 
316
- /** @type {boolean} */
317
- this._destroyed = false;
318
-
319
- // Save any prior tabindex info and make this node untabbable
320
- this.ensureUntabbable();
321
- }
313
+ /**
314
+ * @type {!Set<!InertRoot>} The set of descendant inert roots.
315
+ * If and only if this set becomes empty, this node is no longer inert.
316
+ */
317
+ this._inertRoots = new Set([inertRoot]);
322
318
 
323
- /**
324
- * Call this whenever this object is about to become obsolete.
325
- * This makes the managed node focusable again and deletes all of the previously stored state.
326
- */
327
- destructor() {
328
- this._throwIfDestroyed();
319
+ /** @type {?number} */
320
+ this._savedTabIndex = null;
329
321
 
330
- if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
331
- const element = /** @type {!Element} */ (this._node);
332
- if (this._savedTabIndex !== null) {
333
- element.setAttribute('tabindex', this._savedTabIndex);
334
- } else {
335
- element.removeAttribute('tabindex');
336
- }
322
+ /** @type {boolean} */
323
+ this._destroyed = false;
337
324
 
338
- // Use `delete` to restore native focus method.
339
- if (this._overrodeFocusMethod) {
340
- delete element.focus;
341
- }
325
+ // Save any prior tabindex info and make this node untabbable
326
+ this.ensureUntabbable();
342
327
  }
343
328
 
344
- // See note in InertRoot.destructor for why we cast these nulls to ANY.
345
- this._node = /** @type {?} */ (null);
346
- this._inertRoots = /** @type {?} */ (null);
347
- this._destroyed = true;
348
- }
329
+ /**
330
+ * Call this whenever this object is about to become obsolete.
331
+ * This makes the managed node focusable again and deletes all of the previously stored state.
332
+ */
333
+ destructor() {
334
+ this._throwIfDestroyed();
335
+
336
+ if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
337
+ const element = /** @type {!Element} */ (this._node);
338
+ if (this._savedTabIndex !== null) {
339
+ element.setAttribute('tabindex', this._savedTabIndex);
340
+ } else {
341
+ element.removeAttribute('tabindex');
342
+ }
349
343
 
350
- /**
351
- * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
352
- * If the object has been destroyed, any attempt to access it will cause an exception.
353
- */
354
- get destroyed() {
355
- return /** @type {!InertNode} */ (this)._destroyed;
356
- }
344
+ // Use `delete` to restore native focus method.
345
+ if (this._overrodeFocusMethod) {
346
+ delete element.focus;
347
+ }
348
+ }
357
349
 
358
- /**
359
- * Throw if user tries to access destroyed InertNode.
360
- */
361
- _throwIfDestroyed() {
362
- if (this.destroyed) {
363
- throw new Error('Trying to access destroyed InertNode');
350
+ // See note in InertRoot.destructor for why we cast these nulls to ANY.
351
+ this._node = /** @type {?} */ (null);
352
+ this._inertRoots = /** @type {?} */ (null);
353
+ this._destroyed = true;
364
354
  }
365
- }
366
355
 
367
- /** @return {boolean} */
368
- get hasSavedTabIndex() {
369
- return this._savedTabIndex !== null;
370
- }
356
+ /**
357
+ * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
358
+ * If the object has been destroyed, any attempt to access it will cause an exception.
359
+ */
360
+ get destroyed() {
361
+ return /** @type {!InertNode} */ (this)._destroyed;
362
+ }
371
363
 
372
- /** @return {!Node} */
373
- get node() {
374
- this._throwIfDestroyed();
375
- return this._node;
376
- }
364
+ /**
365
+ * Throw if user tries to access destroyed InertNode.
366
+ */
367
+ _throwIfDestroyed() {
368
+ if (this.destroyed) {
369
+ throw new Error('Trying to access destroyed InertNode');
370
+ }
371
+ }
377
372
 
378
- /** @param {?number} tabIndex */
379
- set savedTabIndex(tabIndex) {
380
- this._throwIfDestroyed();
381
- this._savedTabIndex = tabIndex;
382
- }
373
+ /** @return {boolean} */
374
+ get hasSavedTabIndex() {
375
+ return this._savedTabIndex !== null;
376
+ }
383
377
 
384
- /** @return {?number} */
385
- get savedTabIndex() {
386
- this._throwIfDestroyed();
387
- return this._savedTabIndex;
388
- }
378
+ /** @return {!Node} */
379
+ get node() {
380
+ this._throwIfDestroyed();
381
+ return this._node;
382
+ }
389
383
 
390
- /** Save the existing tabindex value and make the node untabbable and unfocusable */
391
- ensureUntabbable() {
392
- if (this.node.nodeType !== Node.ELEMENT_NODE) {
393
- return;
384
+ /** @param {?number} tabIndex */
385
+ set savedTabIndex(tabIndex) {
386
+ this._throwIfDestroyed();
387
+ this._savedTabIndex = tabIndex;
388
+ }
389
+
390
+ /** @return {?number} */
391
+ get savedTabIndex() {
392
+ this._throwIfDestroyed();
393
+ return this._savedTabIndex;
394
394
  }
395
- const element = /** @type {!Element} */ (this.node);
396
- if (matches.call(element, _focusableElementsString)) {
397
- if (/** @type {!HTMLElement} */ (element).tabIndex === -1 &&
398
- this.hasSavedTabIndex) {
395
+
396
+ /** Save the existing tabindex value and make the node untabbable and unfocusable */
397
+ ensureUntabbable() {
398
+ if (this.node.nodeType !== Node.ELEMENT_NODE) {
399
399
  return;
400
400
  }
401
+ const element = /** @type {!Element} */ (this.node);
402
+ if (matches.call(element, _focusableElementsString)) {
403
+ if (/** @type {!HTMLElement} */ (element).tabIndex === -1 &&
404
+ this.hasSavedTabIndex) {
405
+ return;
406
+ }
401
407
 
402
- if (element.hasAttribute('tabindex')) {
408
+ if (element.hasAttribute('tabindex')) {
409
+ this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
410
+ }
411
+ element.setAttribute('tabindex', '-1');
412
+ if (element.nodeType === Node.ELEMENT_NODE) {
413
+ element.focus = function() {};
414
+ this._overrodeFocusMethod = true;
415
+ }
416
+ } else if (element.hasAttribute('tabindex')) {
403
417
  this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
418
+ element.removeAttribute('tabindex');
404
419
  }
405
- element.setAttribute('tabindex', '-1');
406
- if (element.nodeType === Node.ELEMENT_NODE) {
407
- element.focus = function() {};
408
- this._overrodeFocusMethod = true;
409
- }
410
- } else if (element.hasAttribute('tabindex')) {
411
- this._savedTabIndex = /** @type {!HTMLElement} */ (element).tabIndex;
412
- element.removeAttribute('tabindex');
413
420
  }
414
- }
415
421
 
416
- /**
417
- * Add another inert root to this inert node's set of managing inert roots.
418
- * @param {!InertRoot} inertRoot
419
- */
420
- addInertRoot(inertRoot) {
421
- this._throwIfDestroyed();
422
- this._inertRoots.add(inertRoot);
423
- }
422
+ /**
423
+ * Add another inert root to this inert node's set of managing inert roots.
424
+ * @param {!InertRoot} inertRoot
425
+ */
426
+ addInertRoot(inertRoot) {
427
+ this._throwIfDestroyed();
428
+ this._inertRoots.add(inertRoot);
429
+ }
424
430
 
425
- /**
426
- * Remove the given inert root from this inert node's set of managing inert roots.
427
- * If the set of managing inert roots becomes empty, this node is no longer inert,
428
- * so the object should be destroyed.
429
- * @param {!InertRoot} inertRoot
430
- */
431
- removeInertRoot(inertRoot) {
432
- this._throwIfDestroyed();
433
- this._inertRoots.delete(inertRoot);
434
- if (this._inertRoots.size === 0) {
435
- this.destructor();
431
+ /**
432
+ * Remove the given inert root from this inert node's set of managing inert roots.
433
+ * If the set of managing inert roots becomes empty, this node is no longer inert,
434
+ * so the object should be destroyed.
435
+ * @param {!InertRoot} inertRoot
436
+ */
437
+ removeInertRoot(inertRoot) {
438
+ this._throwIfDestroyed();
439
+ this._inertRoots.delete(inertRoot);
440
+ if (this._inertRoots.size === 0) {
441
+ this.destructor();
442
+ }
436
443
  }
437
444
  }
438
- }
439
445
 
440
- /**
441
- * InertManager is a per-document singleton object which manages all inert roots and nodes.
442
- *
443
- * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
444
- * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
445
- * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
446
- * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
447
- * is created for each such node, via the `_managedNodes` map.
448
- */
449
- class InertManager {
450
446
  /**
451
- * @param {!Document} document
447
+ * InertManager is a per-document singleton object which manages all inert roots and nodes.
448
+ *
449
+ * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
450
+ * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
451
+ * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
452
+ * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
453
+ * is created for each such node, via the `_managedNodes` map.
452
454
  */
453
- constructor(document) {
454
- if (!document) {
455
- throw new Error('Missing required argument; InertManager needs to wrap a document.');
456
- }
455
+ class InertManager {
456
+ /**
457
+ * @param {!Document} document
458
+ */
459
+ constructor(document) {
460
+ if (!document) {
461
+ throw new Error('Missing required argument; InertManager needs to wrap a document.');
462
+ }
457
463
 
458
- /** @type {!Document} */
459
- this._document = document;
464
+ /** @type {!Document} */
465
+ this._document = document;
466
+
467
+ /**
468
+ * All managed nodes known to this InertManager. In a map to allow looking up by Node.
469
+ * @type {!Map<!Node, !InertNode>}
470
+ */
471
+ this._managedNodes = new Map();
472
+
473
+ /**
474
+ * All inert roots known to this InertManager. In a map to allow looking up by Node.
475
+ * @type {!Map<!Node, !InertRoot>}
476
+ */
477
+ this._inertRoots = new Map();
478
+
479
+ /**
480
+ * Observer for mutations on `document.body`.
481
+ * @type {!MutationObserver}
482
+ */
483
+ this._observer = new MutationObserver(this._watchForInert.bind(this));
484
+
485
+ // Add inert style.
486
+ addInertStyle(document.head || document.body || document.documentElement);
487
+
488
+ // Wait for document to be loaded.
489
+ if (document.readyState === 'loading') {
490
+ document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
491
+ } else {
492
+ this._onDocumentLoaded();
493
+ }
494
+ }
460
495
 
461
496
  /**
462
- * All managed nodes known to this InertManager. In a map to allow looking up by Node.
463
- * @type {!Map<!Node, !InertNode>}
497
+ * Set whether the given element should be an inert root or not.
498
+ * @param {!Element} root
499
+ * @param {boolean} inert
464
500
  */
465
- this._managedNodes = new Map();
501
+ setInert(root, inert) {
502
+ if (inert) {
503
+ if (this._inertRoots.has(root)) { // element is already inert
504
+ return;
505
+ }
506
+
507
+ const inertRoot = new InertRoot(root, this);
508
+ root.setAttribute('inert', '');
509
+ this._inertRoots.set(root, inertRoot);
510
+ // If not contained in the document, it must be in a shadowRoot.
511
+ // Ensure inert styles are added there.
512
+ if (!this._document.body.contains(root)) {
513
+ let parent = root.parentNode;
514
+ while (parent) {
515
+ if (parent.nodeType === 11) {
516
+ addInertStyle(parent);
517
+ }
518
+ parent = parent.parentNode;
519
+ }
520
+ }
521
+ } else {
522
+ if (!this._inertRoots.has(root)) { // element is already non-inert
523
+ return;
524
+ }
525
+
526
+ const inertRoot = this._inertRoots.get(root);
527
+ inertRoot.destructor();
528
+ this._inertRoots.delete(root);
529
+ root.removeAttribute('inert');
530
+ }
531
+ }
466
532
 
467
533
  /**
468
- * All inert roots known to this InertManager. In a map to allow looking up by Node.
469
- * @type {!Map<!Node, !InertRoot>}
534
+ * Get the InertRoot object corresponding to the given inert root element, if any.
535
+ * @param {!Node} element
536
+ * @return {!InertRoot|undefined}
470
537
  */
471
- this._inertRoots = new Map();
538
+ getInertRoot(element) {
539
+ return this._inertRoots.get(element);
540
+ }
472
541
 
473
542
  /**
474
- * Observer for mutations on `document.body`.
475
- * @type {!MutationObserver}
543
+ * Register the given InertRoot as managing the given node.
544
+ * In the case where the node has a previously existing inert root, this inert root will
545
+ * be added to its set of inert roots.
546
+ * @param {!Node} node
547
+ * @param {!InertRoot} inertRoot
548
+ * @return {!InertNode} inertNode
476
549
  */
477
- this._observer = new MutationObserver(this._watchForInert.bind(this));
550
+ register(node, inertRoot) {
551
+ let inertNode = this._managedNodes.get(node);
552
+ if (inertNode !== undefined) { // node was already in an inert subtree
553
+ inertNode.addInertRoot(inertRoot);
554
+ } else {
555
+ inertNode = new InertNode(node, inertRoot);
556
+ }
478
557
 
479
- // Add inert style.
480
- addInertStyle(document.head || document.body || document.documentElement);
558
+ this._managedNodes.set(node, inertNode);
481
559
 
482
- // Wait for document to be loaded.
483
- if (document.readyState === 'loading') {
484
- document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
485
- } else {
486
- this._onDocumentLoaded();
560
+ return inertNode;
487
561
  }
488
- }
489
562
 
490
- /**
491
- * Set whether the given element should be an inert root or not.
492
- * @param {!Element} root
493
- * @param {boolean} inert
494
- */
495
- setInert(root, inert) {
496
- if (inert) {
497
- if (this._inertRoots.has(root)) { // element is already inert
498
- return;
563
+ /**
564
+ * De-register the given InertRoot as managing the given inert node.
565
+ * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
566
+ * node from the InertManager's set of managed nodes if it is destroyed.
567
+ * If the node is not currently managed, this is essentially a no-op.
568
+ * @param {!Node} node
569
+ * @param {!InertRoot} inertRoot
570
+ * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
571
+ */
572
+ deregister(node, inertRoot) {
573
+ const inertNode = this._managedNodes.get(node);
574
+ if (!inertNode) {
575
+ return null;
499
576
  }
500
577
 
501
- const inertRoot = new InertRoot(root, this);
502
- root.setAttribute('inert', '');
503
- this._inertRoots.set(root, inertRoot);
504
- // If not contained in the document, it must be in a shadowRoot.
505
- // Ensure inert styles are added there.
506
- if (!this._document.body.contains(root)) {
507
- let parent = root.parentNode;
508
- while (parent) {
509
- if (parent.nodeType === 11) {
510
- addInertStyle(parent);
511
- }
512
- parent = parent.parentNode;
513
- }
514
- }
515
- } else {
516
- if (!this._inertRoots.has(root)) { // element is already non-inert
517
- return;
578
+ inertNode.removeInertRoot(inertRoot);
579
+ if (inertNode.destroyed) {
580
+ this._managedNodes.delete(node);
518
581
  }
519
582
 
520
- const inertRoot = this._inertRoots.get(root);
521
- inertRoot.destructor();
522
- this._inertRoots.delete(root);
523
- root.removeAttribute('inert');
583
+ return inertNode;
524
584
  }
525
- }
526
585
 
527
- /**
528
- * Get the InertRoot object corresponding to the given inert root element, if any.
529
- * @param {!Node} element
530
- * @return {!InertRoot|undefined}
531
- */
532
- getInertRoot(element) {
533
- return this._inertRoots.get(element);
534
- }
535
-
536
- /**
537
- * Register the given InertRoot as managing the given node.
538
- * In the case where the node has a previously existing inert root, this inert root will
539
- * be added to its set of inert roots.
540
- * @param {!Node} node
541
- * @param {!InertRoot} inertRoot
542
- * @return {!InertNode} inertNode
543
- */
544
- register(node, inertRoot) {
545
- let inertNode = this._managedNodes.get(node);
546
- if (inertNode !== undefined) { // node was already in an inert subtree
547
- inertNode.addInertRoot(inertRoot);
548
- } else {
549
- inertNode = new InertNode(node, inertRoot);
586
+ /**
587
+ * Callback used when document has finished loading.
588
+ */
589
+ _onDocumentLoaded() {
590
+ // Find all inert roots in document and make them actually inert.
591
+ const inertElements = slice.call(this._document.querySelectorAll('[inert]'));
592
+ inertElements.forEach(function(inertElement) {
593
+ this.setInert(inertElement, true);
594
+ }, this);
595
+
596
+ // Comment this out to use programmatic API only.
597
+ this._observer.observe(this._document.body || this._document.documentElement, {attributes: true, subtree: true, childList: true});
550
598
  }
551
599
 
552
- this._managedNodes.set(node, inertNode);
553
-
554
- return inertNode;
600
+ /**
601
+ * Callback used when mutation observer detects attribute changes.
602
+ * @param {!Array<!MutationRecord>} records
603
+ * @param {!MutationObserver} self
604
+ */
605
+ _watchForInert(records, self) {
606
+ const _this = this;
607
+ records.forEach(function(record) {
608
+ switch (record.type) {
609
+ case 'childList':
610
+ slice.call(record.addedNodes).forEach(function(node) {
611
+ if (node.nodeType !== Node.ELEMENT_NODE) {
612
+ return;
613
+ }
614
+ const inertElements = slice.call(node.querySelectorAll('[inert]'));
615
+ if (matches.call(node, '[inert]')) {
616
+ inertElements.unshift(node);
617
+ }
618
+ inertElements.forEach(function(inertElement) {
619
+ this.setInert(inertElement, true);
620
+ }, _this);
621
+ }, _this);
622
+ break;
623
+ case 'attributes':
624
+ if (record.attributeName !== 'inert') {
625
+ return;
626
+ }
627
+ const target = /** @type {!Element} */ (record.target);
628
+ const inert = target.hasAttribute('inert');
629
+ _this.setInert(target, inert);
630
+ break;
631
+ }
632
+ }, this);
633
+ }
555
634
  }
556
635
 
557
636
  /**
558
- * De-register the given InertRoot as managing the given inert node.
559
- * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
560
- * node from the InertManager's set of managed nodes if it is destroyed.
561
- * If the node is not currently managed, this is essentially a no-op.
637
+ * Recursively walk the composed tree from |node|.
562
638
  * @param {!Node} node
563
- * @param {!InertRoot} inertRoot
564
- * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
639
+ * @param {(function (!Element))=} callback Callback to be called for each element traversed,
640
+ * before descending into child nodes.
641
+ * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
565
642
  */
566
- deregister(node, inertRoot) {
567
- const inertNode = this._managedNodes.get(node);
568
- if (!inertNode) {
569
- return null;
570
- }
571
-
572
- inertNode.removeInertRoot(inertRoot);
573
- if (inertNode.destroyed) {
574
- this._managedNodes.delete(node);
575
- }
576
-
577
- return inertNode;
578
- }
643
+ function composedTreeWalk(node, callback, shadowRootAncestor) {
644
+ if (node.nodeType == Node.ELEMENT_NODE) {
645
+ const element = /** @type {!Element} */ (node);
646
+ if (callback) {
647
+ callback(element);
648
+ }
579
649
 
580
- /**
581
- * Callback used when document has finished loading.
582
- */
583
- _onDocumentLoaded() {
584
- // Find all inert roots in document and make them actually inert.
585
- const inertElements = slice.call(this._document.querySelectorAll('[inert]'));
586
- inertElements.forEach(function(inertElement) {
587
- this.setInert(inertElement, true);
588
- }, this);
589
-
590
- // Comment this out to use programmatic API only.
591
- this._observer.observe(this._document.body, {attributes: true, subtree: true, childList: true});
592
- }
650
+ // Descend into node:
651
+ // If it has a ShadowRoot, ignore all child elements - these will be picked
652
+ // up by the <content> or <shadow> elements. Descend straight into the
653
+ // ShadowRoot.
654
+ const shadowRoot = /** @type {!HTMLElement} */ (element).shadowRoot;
655
+ if (shadowRoot) {
656
+ composedTreeWalk(shadowRoot, callback, shadowRoot);
657
+ return;
658
+ }
593
659
 
594
- /**
595
- * Callback used when mutation observer detects attribute changes.
596
- * @param {!Array<!MutationRecord>} records
597
- * @param {!MutationObserver} self
598
- */
599
- _watchForInert(records, self) {
600
- const _this = this;
601
- records.forEach(function(record) {
602
- switch (record.type) {
603
- case 'childList':
604
- slice.call(record.addedNodes).forEach(function(node) {
605
- if (node.nodeType !== Node.ELEMENT_NODE) {
606
- return;
607
- }
608
- const inertElements = slice.call(node.querySelectorAll('[inert]'));
609
- if (matches.call(node, '[inert]')) {
610
- inertElements.unshift(node);
611
- }
612
- inertElements.forEach(function(inertElement) {
613
- this.setInert(inertElement, true);
614
- }, _this);
615
- }, _this);
616
- break;
617
- case 'attributes':
618
- if (record.attributeName !== 'inert') {
619
- return;
660
+ // If it is a <content> element, descend into distributed elements - these
661
+ // are elements from outside the shadow root which are rendered inside the
662
+ // shadow DOM.
663
+ if (element.localName == 'content') {
664
+ const content = /** @type {!HTMLContentElement} */ (element);
665
+ // Verifies if ShadowDom v0 is supported.
666
+ const distributedNodes = content.getDistributedNodes ?
667
+ content.getDistributedNodes() : [];
668
+ for (let i = 0; i < distributedNodes.length; i++) {
669
+ composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
620
670
  }
621
- const target = /** @type {!Element} */ (record.target);
622
- const inert = target.hasAttribute('inert');
623
- _this.setInert(target, inert);
624
- break;
671
+ return;
625
672
  }
626
- }, this);
627
- }
628
- }
629
-
630
- /**
631
- * Recursively walk the composed tree from |node|.
632
- * @param {!Node} node
633
- * @param {(function (!Element))=} callback Callback to be called for each element traversed,
634
- * before descending into child nodes.
635
- * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
636
- */
637
- function composedTreeWalk(node, callback, shadowRootAncestor) {
638
- if (node.nodeType == Node.ELEMENT_NODE) {
639
- const element = /** @type {!Element} */ (node);
640
- if (callback) {
641
- callback(element);
642
- }
643
-
644
- // Descend into node:
645
- // If it has a ShadowRoot, ignore all child elements - these will be picked
646
- // up by the <content> or <shadow> elements. Descend straight into the
647
- // ShadowRoot.
648
- const shadowRoot = /** @type {!HTMLElement} */ (element).shadowRoot;
649
- if (shadowRoot) {
650
- composedTreeWalk(shadowRoot, callback, shadowRoot);
651
- return;
652
- }
653
673
 
654
- // If it is a <content> element, descend into distributed elements - these
655
- // are elements from outside the shadow root which are rendered inside the
656
- // shadow DOM.
657
- if (element.localName == 'content') {
658
- const content = /** @type {!HTMLContentElement} */ (element);
659
- // Verifies if ShadowDom v0 is supported.
660
- const distributedNodes = content.getDistributedNodes ?
661
- content.getDistributedNodes() : [];
662
- for (let i = 0; i < distributedNodes.length; i++) {
663
- composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
674
+ // If it is a <slot> element, descend into assigned nodes - these
675
+ // are elements from outside the shadow root which are rendered inside the
676
+ // shadow DOM.
677
+ if (element.localName == 'slot') {
678
+ const slot = /** @type {!HTMLSlotElement} */ (element);
679
+ // Verify if ShadowDom v1 is supported.
680
+ const distributedNodes = slot.assignedNodes ?
681
+ slot.assignedNodes({flatten: true}) : [];
682
+ for (let i = 0; i < distributedNodes.length; i++) {
683
+ composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
684
+ }
685
+ return;
664
686
  }
665
- return;
666
687
  }
667
688
 
668
- // If it is a <slot> element, descend into assigned nodes - these
669
- // are elements from outside the shadow root which are rendered inside the
670
- // shadow DOM.
671
- if (element.localName == 'slot') {
672
- const slot = /** @type {!HTMLSlotElement} */ (element);
673
- // Verify if ShadowDom v1 is supported.
674
- const distributedNodes = slot.assignedNodes ?
675
- slot.assignedNodes({flatten: true}) : [];
676
- for (let i = 0; i < distributedNodes.length; i++) {
677
- composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
678
- }
679
- return;
689
+ // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
690
+ // element, nor a <shadow> element recurse normally.
691
+ let child = node.firstChild;
692
+ while (child != null) {
693
+ composedTreeWalk(child, callback, shadowRootAncestor);
694
+ child = child.nextSibling;
680
695
  }
681
696
  }
682
697
 
683
- // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
684
- // element, nor a <shadow> element recurse normally.
685
- let child = node.firstChild;
686
- while (child != null) {
687
- composedTreeWalk(child, callback, shadowRootAncestor);
688
- child = child.nextSibling;
689
- }
690
- }
691
-
692
- /**
693
- * Adds a style element to the node containing the inert specific styles
694
- * @param {!Node} node
695
- */
696
- function addInertStyle(node) {
697
- if (node.querySelector('style#inert-style')) {
698
- return;
699
- }
700
- const style = document.createElement('style');
701
- style.setAttribute('id', 'inert-style');
702
- style.textContent = '\n'+
703
- '[inert] {\n' +
704
- ' pointer-events: none;\n' +
705
- ' cursor: default;\n' +
706
- '}\n' +
707
- '\n' +
708
- '[inert], [inert] * {\n' +
709
- ' user-select: none;\n' +
710
- ' -webkit-user-select: none;\n' +
711
- ' -moz-user-select: none;\n' +
712
- ' -ms-user-select: none;\n' +
713
- '}\n';
714
- node.appendChild(style);
715
- }
716
-
717
- /** @type {!InertManager} */
718
- const inertManager = new InertManager(document);
719
-
720
- if (!Element.prototype.hasOwnProperty('inert')) {
721
- Object.defineProperty(Element.prototype, 'inert', {
722
- enumerable: true,
723
- /** @this {!Element} */
724
- get: function() {
725
- return this.hasAttribute('inert');
726
- },
727
- /** @this {!Element} */
728
- set: function(inert) {
729
- inertManager.setInert(this, inert);
730
- },
731
- });
732
- }
698
+ /**
699
+ * Adds a style element to the node containing the inert specific styles
700
+ * @param {!Node} node
701
+ */
702
+ function addInertStyle(node) {
703
+ if (node.querySelector('style#inert-style, link#inert-style')) {
704
+ return;
705
+ }
706
+ const style = document.createElement('style');
707
+ style.setAttribute('id', 'inert-style');
708
+ style.textContent = '\n'+
709
+ '[inert] {\n' +
710
+ ' pointer-events: none;\n' +
711
+ ' cursor: default;\n' +
712
+ '}\n' +
713
+ '\n' +
714
+ '[inert], [inert] * {\n' +
715
+ ' -webkit-user-select: none;\n' +
716
+ ' -moz-user-select: none;\n' +
717
+ ' -ms-user-select: none;\n' +
718
+ ' user-select: none;\n' +
719
+ '}\n';
720
+ node.appendChild(style);
721
+ }
722
+
723
+ if (!Element.prototype.hasOwnProperty('inert')) {
724
+ /** @type {!InertManager} */
725
+ const inertManager = new InertManager(document);
726
+
727
+ Object.defineProperty(Element.prototype, 'inert', {
728
+ enumerable: true,
729
+ /** @this {!Element} */
730
+ get: function() {
731
+ return this.hasAttribute('inert');
732
+ },
733
+ /** @this {!Element} */
734
+ set: function(inert) {
735
+ inertManager.setInert(this, inert);
736
+ },
737
+ });
738
+ }
739
+ })();