monaco-editor-core 0.55.0-dev-20251014 → 0.55.0-dev-20251016

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.
Files changed (207) hide show
  1. package/esm/nls.keys.json +1 -1
  2. package/esm/nls.messages.cs.js +9 -0
  3. package/esm/nls.messages.de.js +2 -2
  4. package/esm/nls.messages.es.js +2 -2
  5. package/esm/nls.messages.fr.js +2 -2
  6. package/esm/nls.messages.it.js +2 -2
  7. package/esm/nls.messages.ja.js +2 -2
  8. package/esm/nls.messages.js +1 -1
  9. package/esm/nls.messages.json +1 -1
  10. package/esm/nls.messages.ko.js +2 -2
  11. package/esm/nls.messages.pl.js +9 -0
  12. package/esm/nls.messages.pt-br.js +9 -0
  13. package/esm/nls.messages.ru.js +2 -2
  14. package/esm/nls.messages.tr.js +9 -0
  15. package/esm/nls.messages.zh-cn.js +2 -2
  16. package/esm/nls.messages.zh-tw.js +2 -2
  17. package/esm/nls.metadata.json +10 -0
  18. package/esm/vs/base/browser/canIUse.js +0 -1
  19. package/esm/vs/base/browser/canIUse.js.map +1 -1
  20. package/esm/vs/base/browser/domSanitize.js +31 -23
  21. package/esm/vs/base/browser/domSanitize.js.map +1 -1
  22. package/esm/vs/base/browser/dompurify/dompurify.js +188 -385
  23. package/esm/vs/base/browser/markdownRenderer.js +6 -1
  24. package/esm/vs/base/browser/markdownRenderer.js.map +1 -1
  25. package/esm/vs/base/browser/trustedTypes.js.map +1 -1
  26. package/esm/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
  27. package/esm/vs/base/browser/ui/toggle/toggle.js +6 -0
  28. package/esm/vs/base/browser/ui/toggle/toggle.js.map +1 -1
  29. package/esm/vs/base/common/codiconsLibrary.js +1 -0
  30. package/esm/vs/base/common/codiconsLibrary.js.map +1 -1
  31. package/esm/vs/base/common/objects.js +0 -1
  32. package/esm/vs/base/common/objects.js.map +1 -1
  33. package/esm/vs/editor/browser/controller/mouseTarget.js +2 -1
  34. package/esm/vs/editor/browser/controller/mouseTarget.js.map +1 -1
  35. package/esm/vs/editor/browser/coreCommands.js +4 -2
  36. package/esm/vs/editor/browser/coreCommands.js.map +1 -1
  37. package/esm/vs/editor/browser/services/abstractCodeEditorService.js +5 -0
  38. package/esm/vs/editor/browser/services/abstractCodeEditorService.js.map +1 -1
  39. package/esm/vs/editor/browser/services/codeEditorService.js.map +1 -1
  40. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/copySelection.js +104 -0
  41. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/copySelection.js.map +1 -0
  42. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js +3 -3
  43. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js.map +1 -1
  44. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.js +25 -24
  45. package/esm/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.js.map +1 -1
  46. package/esm/vs/editor/browser/widget/diffEditor/diffEditor.contribution.js +5 -5
  47. package/esm/vs/editor/browser/widget/diffEditor/diffEditorWidget.js +7 -3
  48. package/esm/vs/editor/browser/widget/diffEditor/diffEditorWidget.js.map +1 -1
  49. package/esm/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.js +7 -7
  50. package/esm/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.js +2 -2
  51. package/esm/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.js +2 -2
  52. package/esm/vs/editor/browser/widget/diffEditor/registrations.contribution.js +5 -5
  53. package/esm/vs/editor/browser/widget/diffEditor/style.css +11 -0
  54. package/esm/vs/editor/browser/widget/multiDiffEditor/colors.js +3 -3
  55. package/esm/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.js +2 -2
  56. package/esm/vs/editor/common/config/editorConfigurationSchema.js +54 -54
  57. package/esm/vs/editor/common/config/editorOptions.js +406 -406
  58. package/esm/vs/editor/common/core/editorColorRegistry.js +68 -68
  59. package/esm/vs/editor/common/cursor/cursorMoveCommands.js +10 -1
  60. package/esm/vs/editor/common/cursor/cursorMoveCommands.js.map +1 -1
  61. package/esm/vs/editor/common/editorContextKeys.js +46 -46
  62. package/esm/vs/editor/common/languages/modesRegistry.js +1 -1
  63. package/esm/vs/editor/common/languages.js +56 -56
  64. package/esm/vs/editor/common/model/editStack.js +1 -1
  65. package/esm/vs/editor/common/standaloneStrings.js +10 -10
  66. package/esm/vs/editor/common/viewLayout/viewLineRenderer.js +2 -2
  67. package/esm/vs/editor/contrib/anchorSelect/browser/anchorSelect.js +6 -6
  68. package/esm/vs/editor/contrib/bracketMatching/browser/bracketMatching.js +6 -6
  69. package/esm/vs/editor/contrib/caretOperations/browser/caretOperations.js +2 -2
  70. package/esm/vs/editor/contrib/caretOperations/browser/transpose.js +1 -1
  71. package/esm/vs/editor/contrib/clipboard/browser/clipboard.js +17 -17
  72. package/esm/vs/editor/contrib/codeAction/browser/codeAction.js +1 -1
  73. package/esm/vs/editor/contrib/codeAction/browser/codeActionCommands.js +29 -29
  74. package/esm/vs/editor/contrib/codeAction/browser/codeActionContributions.js +3 -3
  75. package/esm/vs/editor/contrib/codeAction/browser/codeActionController.js +3 -3
  76. package/esm/vs/editor/contrib/codeAction/browser/codeActionMenu.js +8 -8
  77. package/esm/vs/editor/contrib/codeAction/browser/lightBulbWidget.js +9 -9
  78. package/esm/vs/editor/contrib/codelens/browser/codelensController.js +2 -2
  79. package/esm/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerCloseButton.js +1 -1
  80. package/esm/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.js +1 -1
  81. package/esm/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerActions.js +7 -7
  82. package/esm/vs/editor/contrib/comment/browser/comment.js +6 -6
  83. package/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js +13 -14
  84. package/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js.map +1 -1
  85. package/esm/vs/editor/contrib/cursorUndo/browser/cursorUndo.js +2 -2
  86. package/esm/vs/editor/contrib/dropOrPasteInto/browser/copyPasteContribution.js +4 -4
  87. package/esm/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.js +9 -9
  88. package/esm/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.js +8 -8
  89. package/esm/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js +3 -3
  90. package/esm/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.js +2 -2
  91. package/esm/vs/editor/contrib/editorState/browser/keybindingCancellation.js +1 -1
  92. package/esm/vs/editor/contrib/find/browser/findController.js +16 -16
  93. package/esm/vs/editor/contrib/find/browser/findWidget.js +26 -26
  94. package/esm/vs/editor/contrib/folding/browser/folding.js +20 -20
  95. package/esm/vs/editor/contrib/folding/browser/foldingDecorations.js +9 -9
  96. package/esm/vs/editor/contrib/folding/browser/foldingDecorations.js.map +1 -1
  97. package/esm/vs/editor/contrib/fontZoom/browser/fontZoom.js +3 -3
  98. package/esm/vs/editor/contrib/format/browser/formatActions.js +2 -2
  99. package/esm/vs/editor/contrib/gotoError/browser/gotoError.js +8 -8
  100. package/esm/vs/editor/contrib/gotoError/browser/gotoErrorWidget.js +14 -14
  101. package/esm/vs/editor/contrib/gotoSymbol/browser/goToCommands.js +39 -39
  102. package/esm/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.js +1 -1
  103. package/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.js +3 -3
  104. package/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.js +3 -3
  105. package/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.js +3 -3
  106. package/esm/vs/editor/contrib/gotoSymbol/browser/referencesModel.js +8 -8
  107. package/esm/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.js +3 -3
  108. package/esm/vs/editor/contrib/gpu/browser/gpuActions.js +4 -4
  109. package/esm/vs/editor/contrib/hover/browser/hoverActionIds.js +2 -2
  110. package/esm/vs/editor/contrib/hover/browser/hoverActions.js +24 -24
  111. package/esm/vs/editor/contrib/hover/browser/markdownHoverParticipant.js +9 -9
  112. package/esm/vs/editor/contrib/hover/browser/markerHoverParticipant.js +5 -5
  113. package/esm/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.js +2 -2
  114. package/esm/vs/editor/contrib/indentation/browser/indentation.js +20 -20
  115. package/esm/vs/editor/contrib/inlayHints/browser/inlayHintsHover.js +8 -8
  116. package/esm/vs/editor/contrib/inlineCompletions/browser/controller/commands.js +19 -19
  117. package/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.js +12 -12
  118. package/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js +15 -10
  119. package/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js.map +1 -1
  120. package/esm/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.js +1 -1
  121. package/esm/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.js +5 -5
  122. package/esm/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.js +6 -6
  123. package/esm/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.js +1 -1
  124. package/esm/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.js +20 -20
  125. package/esm/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.js +1 -1
  126. package/esm/vs/editor/contrib/lineSelection/browser/lineSelection.js +1 -1
  127. package/esm/vs/editor/contrib/linesOperations/browser/linesOperations.js +47 -37
  128. package/esm/vs/editor/contrib/linesOperations/browser/linesOperations.js.map +1 -1
  129. package/esm/vs/editor/contrib/linkedEditing/browser/linkedEditing.js +2 -2
  130. package/esm/vs/editor/contrib/links/browser/links.js +10 -10
  131. package/esm/vs/editor/contrib/message/browser/messageController.js +1 -1
  132. package/esm/vs/editor/contrib/multicursor/browser/multicursor.js +22 -22
  133. package/esm/vs/editor/contrib/parameterHints/browser/parameterHints.js +1 -1
  134. package/esm/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.js +4 -4
  135. package/esm/vs/editor/contrib/peekView/browser/peekView.js +18 -18
  136. package/esm/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.js +1 -1
  137. package/esm/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.js +56 -10
  138. package/esm/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.js.map +1 -1
  139. package/esm/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.js +32 -32
  140. package/esm/vs/editor/contrib/readOnlyMessage/browser/contribution.js +2 -2
  141. package/esm/vs/editor/contrib/rename/browser/rename.js +11 -11
  142. package/esm/vs/editor/contrib/rename/browser/renameWidget.js +7 -7
  143. package/esm/vs/editor/contrib/smartSelect/browser/smartSelect.js +4 -4
  144. package/esm/vs/editor/contrib/snippet/browser/snippetController2.js +4 -4
  145. package/esm/vs/editor/contrib/snippet/browser/snippetVariables.js +4 -4
  146. package/esm/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.js +11 -11
  147. package/esm/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.js +2 -2
  148. package/esm/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.js.map +1 -1
  149. package/esm/vs/editor/contrib/suggest/browser/suggest.js +8 -8
  150. package/esm/vs/editor/contrib/suggest/browser/suggestController.js +10 -10
  151. package/esm/vs/editor/contrib/suggest/browser/suggestWidget.js +17 -17
  152. package/esm/vs/editor/contrib/suggest/browser/suggestWidgetDetails.js +2 -2
  153. package/esm/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.js +2 -2
  154. package/esm/vs/editor/contrib/suggest/browser/wordContextKey.js +1 -1
  155. package/esm/vs/editor/contrib/symbolIcons/browser/symbolIcons.js +33 -33
  156. package/esm/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.js +4 -4
  157. package/esm/vs/editor/contrib/tokenization/browser/tokenization.js +1 -1
  158. package/esm/vs/editor/contrib/unicodeHighlighter/browser/bannerController.js +1 -1
  159. package/esm/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.js +24 -24
  160. package/esm/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.js +5 -5
  161. package/esm/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.js +9 -9
  162. package/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js +3 -3
  163. package/esm/vs/editor/contrib/wordOperations/browser/wordOperations.js +1 -1
  164. package/esm/vs/platform/accessibilitySignal/browser/accessibilitySignalService.js +62 -62
  165. package/esm/vs/platform/action/common/actionCommonCategories.js +6 -6
  166. package/esm/vs/platform/actionWidget/browser/actionList.js +4 -4
  167. package/esm/vs/platform/actionWidget/browser/actionWidget.js +7 -7
  168. package/esm/vs/platform/actions/browser/menuEntryActionViewItem.js +5 -5
  169. package/esm/vs/platform/actions/browser/toolbar.js +2 -2
  170. package/esm/vs/platform/actions/common/actions.js +0 -1
  171. package/esm/vs/platform/actions/common/actions.js.map +1 -1
  172. package/esm/vs/platform/actions/common/menuService.js +2 -2
  173. package/esm/vs/platform/configuration/common/configurationRegistry.js +10 -10
  174. package/esm/vs/platform/contextkey/browser/contextKeyService.js +1 -1
  175. package/esm/vs/platform/contextkey/common/contextkey.js +9 -9
  176. package/esm/vs/platform/contextkey/common/contextkeys.js +9 -9
  177. package/esm/vs/platform/contextkey/common/scanner.js +5 -5
  178. package/esm/vs/platform/history/browser/contextScopedHistoryWidget.js +1 -1
  179. package/esm/vs/platform/keybinding/common/abstractKeybindingService.js +4 -4
  180. package/esm/vs/platform/list/browser/listService.js +27 -27
  181. package/esm/vs/platform/markers/common/markerService.js +2 -2
  182. package/esm/vs/platform/markers/common/markers.js +6 -6
  183. package/esm/vs/platform/quickinput/browser/commandsQuickAccess.js +7 -7
  184. package/esm/vs/platform/quickinput/browser/helpQuickAccess.js +1 -1
  185. package/esm/vs/platform/quickinput/browser/quickInput.js +10 -10
  186. package/esm/vs/platform/quickinput/browser/quickInputActions.js +5 -5
  187. package/esm/vs/platform/quickinput/browser/quickInputController.js +6 -6
  188. package/esm/vs/platform/quickinput/browser/quickInputList.js +1 -1
  189. package/esm/vs/platform/quickinput/browser/quickInputUtils.js +1 -1
  190. package/esm/vs/platform/quickinput/browser/tree/quickInputTreeAccessibilityProvider.js +1 -1
  191. package/esm/vs/platform/quickinput/common/quickInput.js.map +1 -1
  192. package/esm/vs/platform/theme/common/colorUtils.js +2 -2
  193. package/esm/vs/platform/theme/common/colors/baseColors.js +17 -17
  194. package/esm/vs/platform/theme/common/colors/chartsColors.js +8 -8
  195. package/esm/vs/platform/theme/common/colors/editorColors.js +95 -95
  196. package/esm/vs/platform/theme/common/colors/inputColors.js +47 -47
  197. package/esm/vs/platform/theme/common/colors/listColors.js +36 -36
  198. package/esm/vs/platform/theme/common/colors/menuColors.js +7 -7
  199. package/esm/vs/platform/theme/common/colors/minimapColors.js +11 -11
  200. package/esm/vs/platform/theme/common/colors/miscColors.js +15 -15
  201. package/esm/vs/platform/theme/common/colors/quickpickColors.js +9 -9
  202. package/esm/vs/platform/theme/common/colors/searchColors.js +3 -3
  203. package/esm/vs/platform/theme/common/iconRegistry.js +6 -6
  204. package/esm/vs/platform/undoRedo/common/undoRedoService.js +20 -20
  205. package/esm/vs/platform/workspace/common/workspace.js +1 -1
  206. package/package.json +3 -3
  207. package/version.txt +1 -1
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.1.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.7/LICENSE */
1
+ /*! @license DOMPurify 3.2.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.7/LICENSE */
2
2
 
3
3
  const {
4
4
  entries,
@@ -27,18 +27,26 @@ if (!seal) {
27
27
  };
28
28
  }
29
29
  if (!apply) {
30
- apply = function apply(fun, thisValue, args) {
31
- return fun.apply(thisValue, args);
30
+ apply = function apply(func, thisArg) {
31
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
32
+ args[_key - 2] = arguments[_key];
33
+ }
34
+ return func.apply(thisArg, args);
32
35
  };
33
36
  }
34
37
  if (!construct) {
35
- construct = function construct(Func, args) {
38
+ construct = function construct(Func) {
39
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
40
+ args[_key2 - 1] = arguments[_key2];
41
+ }
36
42
  return new Func(...args);
37
43
  };
38
44
  }
39
45
  const arrayForEach = unapply(Array.prototype.forEach);
46
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
40
47
  const arrayPop = unapply(Array.prototype.pop);
41
48
  const arrayPush = unapply(Array.prototype.push);
49
+ const arraySplice = unapply(Array.prototype.splice);
42
50
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
43
51
  const stringToString = unapply(String.prototype.toString);
44
52
  const stringMatch = unapply(String.prototype.match);
@@ -48,44 +56,44 @@ const stringTrim = unapply(String.prototype.trim);
48
56
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
49
57
  const regExpTest = unapply(RegExp.prototype.test);
50
58
  const typeErrorCreate = unconstruct(TypeError);
51
-
52
59
  /**
53
60
  * Creates a new function that calls the given function with a specified thisArg and arguments.
54
61
  *
55
- * @param {Function} func - The function to be wrapped and called.
56
- * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
62
+ * @param func - The function to be wrapped and called.
63
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
57
64
  */
58
65
  function unapply(func) {
59
66
  return function (thisArg) {
60
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
61
- args[_key - 1] = arguments[_key];
67
+ if (thisArg instanceof RegExp) {
68
+ thisArg.lastIndex = 0;
69
+ }
70
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
71
+ args[_key3 - 1] = arguments[_key3];
62
72
  }
63
73
  return apply(func, thisArg, args);
64
74
  };
65
75
  }
66
-
67
76
  /**
68
77
  * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
69
78
  *
70
- * @param {Function} func - The constructor function to be wrapped and called.
71
- * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
79
+ * @param func - The constructor function to be wrapped and called.
80
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
72
81
  */
73
- function unconstruct(func) {
82
+ function unconstruct(Func) {
74
83
  return function () {
75
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
76
- args[_key2] = arguments[_key2];
84
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
85
+ args[_key4] = arguments[_key4];
77
86
  }
78
- return construct(func, args);
87
+ return construct(Func, args);
79
88
  };
80
89
  }
81
-
82
90
  /**
83
91
  * Add properties to a lookup table
84
92
  *
85
- * @param {Object} set - The set to which elements will be added.
86
- * @param {Array} array - The array containing elements to be added to the set.
87
- * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
88
- * @returns {Object} The modified set with added elements.
93
+ * @param set - The set to which elements will be added.
94
+ * @param array - The array containing elements to be added to the set.
95
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
96
+ * @returns The modified set with added elements.
89
97
  */
90
98
  function addToSet(set, array) {
91
99
  let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
@@ -112,12 +120,11 @@ function addToSet(set, array) {
112
120
  }
113
121
  return set;
114
122
  }
115
-
116
123
  /**
117
124
  * Clean up an array to harden against CSPP
118
125
  *
119
- * @param {Array} array - The array to be cleaned.
120
- * @returns {Array} The cleaned version of the array
126
+ * @param array - The array to be cleaned.
127
+ * @returns The cleaned version of the array
121
128
  */
122
129
  function cleanArray(array) {
123
130
  for (let index = 0; index < array.length; index++) {
@@ -128,12 +135,11 @@ function cleanArray(array) {
128
135
  }
129
136
  return array;
130
137
  }
131
-
132
138
  /**
133
139
  * Shallow clone an object
134
140
  *
135
- * @param {Object} object - The object to be cloned.
136
- * @returns {Object} A new object that copies the original.
141
+ * @param object - The object to be cloned.
142
+ * @returns A new object that copies the original.
137
143
  */
138
144
  function clone(object) {
139
145
  const newObject = create(null);
@@ -151,13 +157,12 @@ function clone(object) {
151
157
  }
152
158
  return newObject;
153
159
  }
154
-
155
160
  /**
156
161
  * This method automatically checks if the prop is function or getter and behaves accordingly.
157
162
  *
158
- * @param {Object} object - The object to look up the getter function in its prototype chain.
159
- * @param {String} prop - The property name for which to find the getter function.
160
- * @returns {Function} The getter function found in the prototype chain or a fallback function.
163
+ * @param object - The object to look up the getter function in its prototype chain.
164
+ * @param prop - The property name for which to find the getter function.
165
+ * @returns The getter function found in the prototype chain or a fallback function.
161
166
  */
162
167
  function lookupGetter(object, prop) {
163
168
  while (object !== null) {
@@ -178,25 +183,21 @@ function lookupGetter(object, prop) {
178
183
  return fallbackValue;
179
184
  }
180
185
 
181
- const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
182
-
183
- // SVG
184
- const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
186
+ const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
187
+ const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'slot', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
185
188
  const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
186
-
187
189
  // List of SVG elements that are disallowed by default.
188
190
  // We still need to know them so that we can do namespace
189
191
  // checks properly in case one wants to add them to
190
192
  // allow-list.
191
193
  const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
192
194
  const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
193
-
194
195
  // Similarly to SVG, we want to know all MathML elements,
195
196
  // even those that we disallow by default.
196
197
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
197
198
  const text = freeze(['#text']);
198
199
 
199
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
200
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
200
201
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
201
202
  const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
202
203
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
@@ -204,10 +205,10 @@ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:x
204
205
  // eslint-disable-next-line unicorn/better-regex
205
206
  const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
206
207
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
207
- const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
208
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
208
+ const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
209
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
209
210
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
210
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
211
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
211
212
  );
212
213
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
213
214
  const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
@@ -217,18 +218,19 @@ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
217
218
 
218
219
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
219
220
  __proto__: null,
220
- MUSTACHE_EXPR: MUSTACHE_EXPR,
221
- ERB_EXPR: ERB_EXPR,
222
- TMPLIT_EXPR: TMPLIT_EXPR,
223
- DATA_ATTR: DATA_ATTR,
224
221
  ARIA_ATTR: ARIA_ATTR,
225
- IS_ALLOWED_URI: IS_ALLOWED_URI,
226
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
227
222
  ATTR_WHITESPACE: ATTR_WHITESPACE,
223
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
224
+ DATA_ATTR: DATA_ATTR,
228
225
  DOCTYPE_NAME: DOCTYPE_NAME,
229
- CUSTOM_ELEMENT: CUSTOM_ELEMENT
226
+ ERB_EXPR: ERB_EXPR,
227
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
228
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
229
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
230
+ TMPLIT_EXPR: TMPLIT_EXPR
230
231
  });
231
232
 
233
+ /* eslint-disable @typescript-eslint/indent */
232
234
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
233
235
  const NODE_TYPE = {
234
236
  element: 1,
@@ -249,20 +251,18 @@ const NODE_TYPE = {
249
251
  const getGlobal = function getGlobal() {
250
252
  return typeof window === 'undefined' ? null : window;
251
253
  };
252
-
253
254
  /**
254
255
  * Creates a no-op policy for internal use only.
255
256
  * Don't export this function outside this module!
256
- * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
257
- * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
258
- * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
257
+ * @param trustedTypes The policy factory.
258
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
259
+ * @return The policy created (or null, if Trusted Types
259
260
  * are not supported or creating the policy failed).
260
261
  */
261
262
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
262
263
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
263
264
  return null;
264
265
  }
265
-
266
266
  // Allow the callers to control the unique policy name
267
267
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
268
268
  // Policy creation with duplicate names throws in Trusted Types.
@@ -289,22 +289,25 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
289
289
  return null;
290
290
  }
291
291
  };
292
+ const _createHooksMap = function _createHooksMap() {
293
+ return {
294
+ afterSanitizeAttributes: [],
295
+ afterSanitizeElements: [],
296
+ afterSanitizeShadowDOM: [],
297
+ beforeSanitizeAttributes: [],
298
+ beforeSanitizeElements: [],
299
+ beforeSanitizeShadowDOM: [],
300
+ uponSanitizeAttribute: [],
301
+ uponSanitizeElement: [],
302
+ uponSanitizeShadowNode: []
303
+ };
304
+ };
292
305
  function createDOMPurify() {
293
306
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
294
307
  const DOMPurify = root => createDOMPurify(root);
295
-
296
- /**
297
- * Version label, exposed for easier checks
298
- * if DOMPurify is up to date or not
299
- */
300
- DOMPurify.version = '3.1.7';
301
-
302
- /**
303
- * Array of elements that DOMPurify removed during sanitation.
304
- * Empty if nothing was removed.
305
- */
308
+ DOMPurify.version = '3.2.7';
306
309
  DOMPurify.removed = [];
307
- if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
310
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
308
311
  // Not running in a browser, provide a factory function
309
312
  // so that you can pass your own Window
310
313
  DOMPurify.isSupported = false;
@@ -332,7 +335,6 @@ function createDOMPurify() {
332
335
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
333
336
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
334
337
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
335
-
336
338
  // As per issue #47, the web-components registry is inherited by a
337
339
  // new document created via createHTMLDocument. As per the spec
338
340
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -356,8 +358,7 @@ function createDOMPurify() {
356
358
  const {
357
359
  importNode
358
360
  } = originalDocument;
359
- let hooks = {};
360
-
361
+ let hooks = _createHooksMap();
361
362
  /**
362
363
  * Expose whether this browser supports running the full DOMPurify.
363
364
  */
@@ -375,22 +376,18 @@ function createDOMPurify() {
375
376
  let {
376
377
  IS_ALLOWED_URI: IS_ALLOWED_URI$1
377
378
  } = EXPRESSIONS;
378
-
379
379
  /**
380
380
  * We consider the elements and attributes below to be safe. Ideally
381
381
  * don't add any new ones but feel free to remove unwanted ones.
382
382
  */
383
-
384
383
  /* allowed element names */
385
384
  let ALLOWED_TAGS = null;
386
385
  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
387
-
388
386
  /* Allowed attribute names */
389
387
  let ALLOWED_ATTR = null;
390
388
  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
391
-
392
389
  /*
393
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
390
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
394
391
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
395
392
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
396
393
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
@@ -415,65 +412,49 @@ function createDOMPurify() {
415
412
  value: false
416
413
  }
417
414
  }));
418
-
419
415
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
420
416
  let FORBID_TAGS = null;
421
-
422
417
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
423
418
  let FORBID_ATTR = null;
424
-
425
419
  /* Decide if ARIA attributes are okay */
426
420
  let ALLOW_ARIA_ATTR = true;
427
-
428
421
  /* Decide if custom data attributes are okay */
429
422
  let ALLOW_DATA_ATTR = true;
430
-
431
423
  /* Decide if unknown protocols are okay */
432
424
  let ALLOW_UNKNOWN_PROTOCOLS = false;
433
-
434
425
  /* Decide if self-closing tags in attributes are allowed.
435
426
  * Usually removed due to a mXSS issue in jQuery 3.0 */
436
427
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
437
-
438
428
  /* Output should be safe for common template engines.
439
429
  * This means, DOMPurify removes data attributes, mustaches and ERB
440
430
  */
441
431
  let SAFE_FOR_TEMPLATES = false;
442
-
443
432
  /* Output should be safe even for XML used within HTML and alike.
444
433
  * This means, DOMPurify removes comments when containing risky content.
445
434
  */
446
435
  let SAFE_FOR_XML = true;
447
-
448
436
  /* Decide if document with <html>... should be returned */
449
437
  let WHOLE_DOCUMENT = false;
450
-
451
438
  /* Track whether config is already set on this instance of DOMPurify. */
452
439
  let SET_CONFIG = false;
453
-
454
440
  /* Decide if all elements (e.g. style, script) must be children of
455
441
  * document.body. By default, browsers might move them to document.head */
456
442
  let FORCE_BODY = false;
457
-
458
443
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
459
444
  * string (or a TrustedHTML object if Trusted Types are supported).
460
445
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
461
446
  */
462
447
  let RETURN_DOM = false;
463
-
464
448
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
465
449
  * string (or a TrustedHTML object if Trusted Types are supported) */
466
450
  let RETURN_DOM_FRAGMENT = false;
467
-
468
451
  /* Try to return a Trusted Type object instead of a string, return a string in
469
452
  * case Trusted Types are not supported */
470
453
  let RETURN_TRUSTED_TYPE = false;
471
-
472
454
  /* Output should be free from DOM clobbering attacks?
473
455
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
474
456
  */
475
457
  let SANITIZE_DOM = true;
476
-
477
458
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
478
459
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
479
460
  *
@@ -489,25 +470,19 @@ function createDOMPurify() {
489
470
  */
490
471
  let SANITIZE_NAMED_PROPS = false;
491
472
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
492
-
493
473
  /* Keep element content when removing element? */
494
474
  let KEEP_CONTENT = true;
495
-
496
475
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
497
476
  * of importing it into a new Document and returning a sanitized copy */
498
477
  let IN_PLACE = false;
499
-
500
478
  /* Allow usage of profiles like html, svg and mathMl */
501
479
  let USE_PROFILES = {};
502
-
503
480
  /* Tags to ignore content of when KEEP_CONTENT is true */
504
481
  let FORBID_CONTENTS = null;
505
482
  const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
506
-
507
483
  /* Tags that are safe for data: URIs */
508
484
  let DATA_URI_TAGS = null;
509
485
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
510
-
511
486
  /* Attributes safe for values like "javascript:" */
512
487
  let URI_SAFE_ATTRIBUTES = null;
513
488
  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
@@ -517,32 +492,33 @@ function createDOMPurify() {
517
492
  /* Document namespace */
518
493
  let NAMESPACE = HTML_NAMESPACE;
519
494
  let IS_EMPTY_INPUT = false;
520
-
521
495
  /* Allowed XHTML+XML namespaces */
522
496
  let ALLOWED_NAMESPACES = null;
523
497
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
524
-
498
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
499
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
500
+ // Certain elements are allowed in both SVG and HTML
501
+ // namespace. We need to specify them explicitly
502
+ // so that they don't get erroneously deleted from
503
+ // HTML namespace.
504
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
525
505
  /* Parsing of strict XHTML documents */
526
506
  let PARSER_MEDIA_TYPE = null;
527
507
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
528
508
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
529
509
  let transformCaseFunc = null;
530
-
531
510
  /* Keep a reference to config to pass to hooks */
532
511
  let CONFIG = null;
533
-
534
512
  /* Ideally, do not touch anything below this line */
535
513
  /* ______________________________________________ */
536
-
537
514
  const formElement = document.createElement('form');
538
515
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
539
516
  return testValue instanceof RegExp || testValue instanceof Function;
540
517
  };
541
-
542
518
  /**
543
519
  * _parseConfig
544
520
  *
545
- * @param {Object} cfg optional config literal
521
+ * @param cfg optional config literal
546
522
  */
547
523
  // eslint-disable-next-line complexity
548
524
  const _parseConfig = function _parseConfig() {
@@ -550,42 +526,26 @@ function createDOMPurify() {
550
526
  if (CONFIG && CONFIG === cfg) {
551
527
  return;
552
528
  }
553
-
554
529
  /* Shield configuration object from tampering */
555
530
  if (!cfg || typeof cfg !== 'object') {
556
531
  cfg = {};
557
532
  }
558
-
559
533
  /* Shield configuration object from prototype pollution */
560
534
  cfg = clone(cfg);
561
535
  PARSER_MEDIA_TYPE =
562
536
  // eslint-disable-next-line unicorn/prefer-includes
563
537
  SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
564
-
565
538
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
566
539
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
567
-
568
540
  /* Set configuration parameters */
569
541
  ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
570
542
  ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
571
543
  ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
572
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
573
- // eslint-disable-line indent
574
- cfg.ADD_URI_SAFE_ATTR,
575
- // eslint-disable-line indent
576
- transformCaseFunc // eslint-disable-line indent
577
- ) // eslint-disable-line indent
578
- : DEFAULT_URI_SAFE_ATTRIBUTES;
579
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
580
- // eslint-disable-line indent
581
- cfg.ADD_DATA_URI_TAGS,
582
- // eslint-disable-line indent
583
- transformCaseFunc // eslint-disable-line indent
584
- ) // eslint-disable-line indent
585
- : DEFAULT_DATA_URI_TAGS;
544
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
545
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
586
546
  FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
587
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
588
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
547
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
548
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
589
549
  USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
590
550
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
591
551
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
@@ -604,6 +564,8 @@ function createDOMPurify() {
604
564
  IN_PLACE = cfg.IN_PLACE || false; // Default false
605
565
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
606
566
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
567
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
568
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
607
569
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
608
570
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
609
571
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
@@ -620,7 +582,6 @@ function createDOMPurify() {
620
582
  if (RETURN_DOM_FRAGMENT) {
621
583
  RETURN_DOM = true;
622
584
  }
623
-
624
585
  /* Parse profile info */
625
586
  if (USE_PROFILES) {
626
587
  ALLOWED_TAGS = addToSet({}, text);
@@ -645,7 +606,6 @@ function createDOMPurify() {
645
606
  addToSet(ALLOWED_ATTR, xml);
646
607
  }
647
608
  }
648
-
649
609
  /* Merge configuration parameters */
650
610
  if (cfg.ADD_TAGS) {
651
611
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
@@ -668,17 +628,14 @@ function createDOMPurify() {
668
628
  }
669
629
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
670
630
  }
671
-
672
631
  /* Add #text in case KEEP_CONTENT is set to true */
673
632
  if (KEEP_CONTENT) {
674
633
  ALLOWED_TAGS['#text'] = true;
675
634
  }
676
-
677
635
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
678
636
  if (WHOLE_DOCUMENT) {
679
637
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
680
638
  }
681
-
682
639
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
683
640
  if (ALLOWED_TAGS.table) {
684
641
  addToSet(ALLOWED_TAGS, ['tbody']);
@@ -691,10 +648,8 @@ function createDOMPurify() {
691
648
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
692
649
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
693
650
  }
694
-
695
651
  // Overwrite existing TrustedTypes policy.
696
652
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
697
-
698
653
  // Sign local variables required by `sanitize`.
699
654
  emptyHTML = trustedTypesPolicy.createHTML('');
700
655
  } else {
@@ -702,13 +657,11 @@ function createDOMPurify() {
702
657
  if (trustedTypesPolicy === undefined) {
703
658
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
704
659
  }
705
-
706
660
  // If creating the internal policy succeeded sign internal variables.
707
661
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
708
662
  emptyHTML = trustedTypesPolicy.createHTML('');
709
663
  }
710
664
  }
711
-
712
665
  // Prevent further manipulation of configuration.
713
666
  // Not available in IE8, Safari 5, etc.
714
667
  if (freeze) {
@@ -716,30 +669,19 @@ function createDOMPurify() {
716
669
  }
717
670
  CONFIG = cfg;
718
671
  };
719
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
720
- const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
721
-
722
- // Certain elements are allowed in both SVG and HTML
723
- // namespace. We need to specify them explicitly
724
- // so that they don't get erroneously deleted from
725
- // HTML namespace.
726
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
727
-
728
672
  /* Keep track of all possible SVG and MathML tags
729
673
  * so that we can perform the namespace checks
730
674
  * correctly. */
731
675
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
732
676
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
733
-
734
677
  /**
735
- * @param {Element} element a DOM element whose namespace is being checked
736
- * @returns {boolean} Return false if the element has a
678
+ * @param element a DOM element whose namespace is being checked
679
+ * @returns Return false if the element has a
737
680
  * namespace that a spec-compliant parser would never
738
681
  * return. Return true otherwise.
739
682
  */
740
683
  const _checkValidNamespace = function _checkValidNamespace(element) {
741
684
  let parent = getParentNode(element);
742
-
743
685
  // In JSDOM, if we're inside shadow DOM, then parentNode
744
686
  // can be null. We just simulate parent in this case.
745
687
  if (!parent || !parent.tagName) {
@@ -760,14 +702,12 @@ function createDOMPurify() {
760
702
  if (parent.namespaceURI === HTML_NAMESPACE) {
761
703
  return tagName === 'svg';
762
704
  }
763
-
764
705
  // The only way to switch from MathML to SVG is via`
765
706
  // svg if parent is either <annotation-xml> or MathML
766
707
  // text integration points.
767
708
  if (parent.namespaceURI === MATHML_NAMESPACE) {
768
709
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
769
710
  }
770
-
771
711
  // We only allow elements that are defined in SVG
772
712
  // spec. All others are disallowed in SVG namespace.
773
713
  return Boolean(ALL_SVG_TAGS[tagName]);
@@ -779,13 +719,11 @@ function createDOMPurify() {
779
719
  if (parent.namespaceURI === HTML_NAMESPACE) {
780
720
  return tagName === 'math';
781
721
  }
782
-
783
722
  // The only way to switch from SVG to MathML is via
784
723
  // <math> and HTML integration points
785
724
  if (parent.namespaceURI === SVG_NAMESPACE) {
786
725
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
787
726
  }
788
-
789
727
  // We only allow elements that are defined in MathML
790
728
  // spec. All others are disallowed in MathML namespace.
791
729
  return Boolean(ALL_MATHML_TAGS[tagName]);
@@ -800,28 +738,24 @@ function createDOMPurify() {
800
738
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
801
739
  return false;
802
740
  }
803
-
804
741
  // We disallow tags that are specific for MathML
805
742
  // or SVG and should never appear in HTML namespace
806
743
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
807
744
  }
808
-
809
745
  // For XHTML and XML documents that support custom namespaces
810
746
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
811
747
  return true;
812
748
  }
813
-
814
749
  // The code should never reach this place (this means
815
750
  // that the element somehow got namespace that is not
816
751
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
817
752
  // Return false just in case.
818
753
  return false;
819
754
  };
820
-
821
755
  /**
822
756
  * _forceRemove
823
757
  *
824
- * @param {Node} node a DOM node
758
+ * @param node a DOM node
825
759
  */
826
760
  const _forceRemove = function _forceRemove(node) {
827
761
  arrayPush(DOMPurify.removed, {
@@ -834,46 +768,43 @@ function createDOMPurify() {
834
768
  remove(node);
835
769
  }
836
770
  };
837
-
838
771
  /**
839
772
  * _removeAttribute
840
773
  *
841
- * @param {String} name an Attribute name
842
- * @param {Node} node a DOM node
774
+ * @param name an Attribute name
775
+ * @param element a DOM node
843
776
  */
844
- const _removeAttribute = function _removeAttribute(name, node) {
777
+ const _removeAttribute = function _removeAttribute(name, element) {
845
778
  try {
846
779
  arrayPush(DOMPurify.removed, {
847
- attribute: node.getAttributeNode(name),
848
- from: node
780
+ attribute: element.getAttributeNode(name),
781
+ from: element
849
782
  });
850
783
  } catch (_) {
851
784
  arrayPush(DOMPurify.removed, {
852
785
  attribute: null,
853
- from: node
786
+ from: element
854
787
  });
855
788
  }
856
- node.removeAttribute(name);
857
-
858
- // We void attribute values for unremovable "is"" attributes
859
- if (name === 'is' && !ALLOWED_ATTR[name]) {
789
+ element.removeAttribute(name);
790
+ // We void attribute values for unremovable "is" attributes
791
+ if (name === 'is') {
860
792
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
861
793
  try {
862
- _forceRemove(node);
794
+ _forceRemove(element);
863
795
  } catch (_) {}
864
796
  } else {
865
797
  try {
866
- node.setAttribute(name, '');
798
+ element.setAttribute(name, '');
867
799
  } catch (_) {}
868
800
  }
869
801
  }
870
802
  };
871
-
872
803
  /**
873
804
  * _initDocument
874
805
  *
875
- * @param {String} dirty a string of dirty markup
876
- * @return {Document} a DOM, filled with the dirty markup
806
+ * @param dirty - a string of dirty markup
807
+ * @return a DOM, filled with the dirty markup
877
808
  */
878
809
  const _initDocument = function _initDocument(dirty) {
879
810
  /* Create a HTML document */
@@ -900,7 +831,6 @@ function createDOMPurify() {
900
831
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
901
832
  } catch (_) {}
902
833
  }
903
-
904
834
  /* Use createHTMLDocument in case DOMParser is not available */
905
835
  if (!doc || !doc.documentElement) {
906
836
  doc = implementation.createDocument(NAMESPACE, 'template', null);
@@ -914,112 +844,86 @@ function createDOMPurify() {
914
844
  if (dirty && leadingWhitespace) {
915
845
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
916
846
  }
917
-
918
847
  /* Work on whole document or just its body */
919
848
  if (NAMESPACE === HTML_NAMESPACE) {
920
849
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
921
850
  }
922
851
  return WHOLE_DOCUMENT ? doc.documentElement : body;
923
852
  };
924
-
925
853
  /**
926
854
  * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
927
855
  *
928
- * @param {Node} root The root element or node to start traversing on.
929
- * @return {NodeIterator} The created NodeIterator
856
+ * @param root The root element or node to start traversing on.
857
+ * @return The created NodeIterator
930
858
  */
931
859
  const _createNodeIterator = function _createNodeIterator(root) {
932
860
  return createNodeIterator.call(root.ownerDocument || root, root,
933
861
  // eslint-disable-next-line no-bitwise
934
862
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
935
863
  };
936
-
937
864
  /**
938
865
  * _isClobbered
939
866
  *
940
- * @param {Node} elm element to check for clobbering attacks
941
- * @return {Boolean} true if clobbered, false if safe
867
+ * @param element element to check for clobbering attacks
868
+ * @return true if clobbered, false if safe
942
869
  */
943
- const _isClobbered = function _isClobbered(elm) {
944
- return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
870
+ const _isClobbered = function _isClobbered(element) {
871
+ return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
945
872
  };
946
-
947
873
  /**
948
874
  * Checks whether the given object is a DOM node.
949
875
  *
950
- * @param {Node} object object to check whether it's a DOM node
951
- * @return {Boolean} true is object is a DOM node
876
+ * @param value object to check whether it's a DOM node
877
+ * @return true is object is a DOM node
952
878
  */
953
- const _isNode = function _isNode(object) {
954
- return typeof Node === 'function' && object instanceof Node;
879
+ const _isNode = function _isNode(value) {
880
+ return typeof Node === 'function' && value instanceof Node;
955
881
  };
956
-
957
- /**
958
- * _executeHook
959
- * Execute user configurable hooks
960
- *
961
- * @param {String} entryPoint Name of the hook's entry point
962
- * @param {Node} currentNode node to work on with the hook
963
- * @param {Object} data additional hook parameters
964
- */
965
- const _executeHook = function _executeHook(entryPoint, currentNode, data) {
966
- if (!hooks[entryPoint]) {
967
- return;
968
- }
969
- arrayForEach(hooks[entryPoint], hook => {
882
+ function _executeHooks(hooks, currentNode, data) {
883
+ arrayForEach(hooks, hook => {
970
884
  hook.call(DOMPurify, currentNode, data, CONFIG);
971
885
  });
972
- };
973
-
886
+ }
974
887
  /**
975
888
  * _sanitizeElements
976
889
  *
977
890
  * @protect nodeName
978
891
  * @protect textContent
979
892
  * @protect removeChild
980
- *
981
- * @param {Node} currentNode to check for permission to exist
982
- * @return {Boolean} true if node was killed, false if left alive
893
+ * @param currentNode to check for permission to exist
894
+ * @return true if node was killed, false if left alive
983
895
  */
984
896
  const _sanitizeElements = function _sanitizeElements(currentNode) {
985
897
  let content = null;
986
-
987
898
  /* Execute a hook if present */
988
- _executeHook('beforeSanitizeElements', currentNode, null);
989
-
899
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
990
900
  /* Check if element is clobbered or can clobber */
991
901
  if (_isClobbered(currentNode)) {
992
902
  _forceRemove(currentNode);
993
903
  return true;
994
904
  }
995
-
996
905
  /* Now let's check the element's type and name */
997
906
  const tagName = transformCaseFunc(currentNode.nodeName);
998
-
999
907
  /* Execute a hook if present */
1000
- _executeHook('uponSanitizeElement', currentNode, {
908
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
1001
909
  tagName,
1002
910
  allowedTags: ALLOWED_TAGS
1003
911
  });
1004
-
1005
912
  /* Detect mXSS attempts abusing namespace confusion */
1006
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
913
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
1007
914
  _forceRemove(currentNode);
1008
915
  return true;
1009
916
  }
1010
-
1011
917
  /* Remove any occurrence of processing instructions */
1012
918
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1013
919
  _forceRemove(currentNode);
1014
920
  return true;
1015
921
  }
1016
-
1017
922
  /* Remove any kind of possibly harmful comments */
1018
923
  if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1019
924
  _forceRemove(currentNode);
1020
925
  return true;
1021
926
  }
1022
-
1023
927
  /* Remove element if anything forbids its presence */
1024
928
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1025
929
  /* Check if we have a custom element to handle */
@@ -1031,7 +935,6 @@ function createDOMPurify() {
1031
935
  return false;
1032
936
  }
1033
937
  }
1034
-
1035
938
  /* Keep content except for bad-listed elements */
1036
939
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1037
940
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
@@ -1048,19 +951,16 @@ function createDOMPurify() {
1048
951
  _forceRemove(currentNode);
1049
952
  return true;
1050
953
  }
1051
-
1052
954
  /* Check whether element has a valid namespace */
1053
955
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1054
956
  _forceRemove(currentNode);
1055
957
  return true;
1056
958
  }
1057
-
1058
959
  /* Make sure that older browsers don't get fallback-tag mXSS */
1059
960
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1060
961
  _forceRemove(currentNode);
1061
962
  return true;
1062
963
  }
1063
-
1064
964
  /* Sanitize element content to be template-safe */
1065
965
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1066
966
  /* Get the element's text content */
@@ -1075,19 +975,17 @@ function createDOMPurify() {
1075
975
  currentNode.textContent = content;
1076
976
  }
1077
977
  }
1078
-
1079
978
  /* Execute a hook if present */
1080
- _executeHook('afterSanitizeElements', currentNode, null);
979
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
1081
980
  return false;
1082
981
  };
1083
-
1084
982
  /**
1085
983
  * _isValidAttribute
1086
984
  *
1087
- * @param {string} lcTag Lowercase tag name of containing element.
1088
- * @param {string} lcName Lowercase attribute name.
1089
- * @param {string} value Attribute value.
1090
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
985
+ * @param lcTag Lowercase tag name of containing element.
986
+ * @param lcName Lowercase attribute name.
987
+ * @param value Attribute value.
988
+ * @return Returns true if `value` is valid, otherwise false.
1091
989
  */
1092
990
  // eslint-disable-next-line complexity
1093
991
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
@@ -1095,7 +993,6 @@ function createDOMPurify() {
1095
993
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1096
994
  return false;
1097
995
  }
1098
-
1099
996
  /* Allow valid data-* attributes: At least one character after "-"
1100
997
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1101
998
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
@@ -1105,7 +1002,7 @@ function createDOMPurify() {
1105
1002
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1106
1003
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1107
1004
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1108
- _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
1005
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||
1109
1006
  // Alternative, second condition checks if it's an `is`-attribute, AND
1110
1007
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1111
1008
  lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
@@ -1117,19 +1014,17 @@ function createDOMPurify() {
1117
1014
  } else ;
1118
1015
  return true;
1119
1016
  };
1120
-
1121
1017
  /**
1122
1018
  * _isBasicCustomElement
1123
1019
  * checks if at least one dash is included in tagName, and it's not the first char
1124
1020
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1125
1021
  *
1126
- * @param {string} tagName name of the tag of the node to sanitize
1127
- * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1022
+ * @param tagName name of the tag of the node to sanitize
1023
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1128
1024
  */
1129
1025
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1130
1026
  return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1131
1027
  };
1132
-
1133
1028
  /**
1134
1029
  * _sanitizeAttributes
1135
1030
  *
@@ -1138,27 +1033,26 @@ function createDOMPurify() {
1138
1033
  * @protect removeAttribute
1139
1034
  * @protect setAttribute
1140
1035
  *
1141
- * @param {Node} currentNode to sanitize
1036
+ * @param currentNode to sanitize
1142
1037
  */
1143
1038
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1144
1039
  /* Execute a hook if present */
1145
- _executeHook('beforeSanitizeAttributes', currentNode, null);
1040
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
1146
1041
  const {
1147
1042
  attributes
1148
1043
  } = currentNode;
1149
-
1150
1044
  /* Check if we have attributes; if not we might have a text node */
1151
- if (!attributes) {
1045
+ if (!attributes || _isClobbered(currentNode)) {
1152
1046
  return;
1153
1047
  }
1154
1048
  const hookEvent = {
1155
1049
  attrName: '',
1156
1050
  attrValue: '',
1157
1051
  keepAttr: true,
1158
- allowedAttributes: ALLOWED_ATTR
1052
+ allowedAttributes: ALLOWED_ATTR,
1053
+ forceKeepAttr: undefined
1159
1054
  };
1160
1055
  let l = attributes.length;
1161
-
1162
1056
  /* Go backwards over all attributes; safely remove bad ones */
1163
1057
  while (l--) {
1164
1058
  const attr = attributes[l];
@@ -1168,65 +1062,60 @@ function createDOMPurify() {
1168
1062
  value: attrValue
1169
1063
  } = attr;
1170
1064
  const lcName = transformCaseFunc(name);
1171
- let value = name === 'value' ? attrValue : stringTrim(attrValue);
1172
-
1065
+ const initValue = attrValue;
1066
+ let value = name === 'value' ? initValue : stringTrim(initValue);
1173
1067
  /* Execute a hook if present */
1174
1068
  hookEvent.attrName = lcName;
1175
1069
  hookEvent.attrValue = value;
1176
1070
  hookEvent.keepAttr = true;
1177
1071
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1178
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1072
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
1179
1073
  value = hookEvent.attrValue;
1180
-
1074
+ /* Full DOM Clobbering protection via namespace isolation,
1075
+ * Prefix id and name attributes with `user-content-`
1076
+ */
1077
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1078
+ // Remove the attribute with this value
1079
+ _removeAttribute(name, currentNode);
1080
+ // Prefix the value and later re-create the attribute with the sanitized value
1081
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
1082
+ }
1083
+ /* Work around a security issue with comments inside attributes */
1084
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title|textarea)/i, value)) {
1085
+ _removeAttribute(name, currentNode);
1086
+ continue;
1087
+ }
1088
+ /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
1089
+ if (lcName === 'attributename' && stringMatch(value, 'href')) {
1090
+ _removeAttribute(name, currentNode);
1091
+ continue;
1092
+ }
1181
1093
  /* Did the hooks approve of the attribute? */
1182
1094
  if (hookEvent.forceKeepAttr) {
1183
1095
  continue;
1184
1096
  }
1185
-
1186
- /* Remove attribute */
1187
- _removeAttribute(name, currentNode);
1188
-
1189
1097
  /* Did the hooks approve of the attribute? */
1190
1098
  if (!hookEvent.keepAttr) {
1099
+ _removeAttribute(name, currentNode);
1191
1100
  continue;
1192
1101
  }
1193
-
1194
1102
  /* Work around a security issue in jQuery 3.0 */
1195
1103
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1196
1104
  _removeAttribute(name, currentNode);
1197
1105
  continue;
1198
1106
  }
1199
-
1200
1107
  /* Sanitize attribute content to be template-safe */
1201
1108
  if (SAFE_FOR_TEMPLATES) {
1202
1109
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1203
1110
  value = stringReplace(value, expr, ' ');
1204
1111
  });
1205
1112
  }
1206
-
1207
1113
  /* Is `value` valid for this attribute? */
1208
1114
  const lcTag = transformCaseFunc(currentNode.nodeName);
1209
1115
  if (!_isValidAttribute(lcTag, lcName, value)) {
1210
- continue;
1211
- }
1212
-
1213
- /* Full DOM Clobbering protection via namespace isolation,
1214
- * Prefix id and name attributes with `user-content-`
1215
- */
1216
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1217
- // Remove the attribute with this value
1218
- _removeAttribute(name, currentNode);
1219
-
1220
- // Prefix the value and later re-create the attribute with the sanitized value
1221
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
1222
- }
1223
-
1224
- /* Work around a security issue with comments inside attributes */
1225
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1226
1116
  _removeAttribute(name, currentNode);
1227
1117
  continue;
1228
1118
  }
1229
-
1230
1119
  /* Handle attributes that require Trusted Types */
1231
1120
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1232
1121
  if (namespaceURI) ; else {
@@ -1244,67 +1133,53 @@ function createDOMPurify() {
1244
1133
  }
1245
1134
  }
1246
1135
  }
1247
-
1248
1136
  /* Handle invalid data-* attribute set by try-catching it */
1249
- try {
1250
- if (namespaceURI) {
1251
- currentNode.setAttributeNS(namespaceURI, name, value);
1252
- } else {
1253
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1254
- currentNode.setAttribute(name, value);
1255
- }
1256
- if (_isClobbered(currentNode)) {
1257
- _forceRemove(currentNode);
1258
- } else {
1259
- arrayPop(DOMPurify.removed);
1137
+ if (value !== initValue) {
1138
+ try {
1139
+ if (namespaceURI) {
1140
+ currentNode.setAttributeNS(namespaceURI, name, value);
1141
+ } else {
1142
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1143
+ currentNode.setAttribute(name, value);
1144
+ }
1145
+ if (_isClobbered(currentNode)) {
1146
+ _forceRemove(currentNode);
1147
+ } else {
1148
+ arrayPop(DOMPurify.removed);
1149
+ }
1150
+ } catch (_) {
1151
+ _removeAttribute(name, currentNode);
1260
1152
  }
1261
- } catch (_) {}
1153
+ }
1262
1154
  }
1263
-
1264
1155
  /* Execute a hook if present */
1265
- _executeHook('afterSanitizeAttributes', currentNode, null);
1156
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
1266
1157
  };
1267
-
1268
1158
  /**
1269
1159
  * _sanitizeShadowDOM
1270
1160
  *
1271
- * @param {DocumentFragment} fragment to iterate over recursively
1161
+ * @param fragment to iterate over recursively
1272
1162
  */
1273
1163
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1274
1164
  let shadowNode = null;
1275
1165
  const shadowIterator = _createNodeIterator(fragment);
1276
-
1277
1166
  /* Execute a hook if present */
1278
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
1167
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
1279
1168
  while (shadowNode = shadowIterator.nextNode()) {
1280
1169
  /* Execute a hook if present */
1281
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
1282
-
1170
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
1283
1171
  /* Sanitize tags and elements */
1284
- if (_sanitizeElements(shadowNode)) {
1285
- continue;
1286
- }
1287
-
1172
+ _sanitizeElements(shadowNode);
1173
+ /* Check attributes next */
1174
+ _sanitizeAttributes(shadowNode);
1288
1175
  /* Deep shadow DOM detected */
1289
1176
  if (shadowNode.content instanceof DocumentFragment) {
1290
1177
  _sanitizeShadowDOM(shadowNode.content);
1291
1178
  }
1292
-
1293
- /* Check attributes, sanitize if necessary */
1294
- _sanitizeAttributes(shadowNode);
1295
1179
  }
1296
-
1297
1180
  /* Execute a hook if present */
1298
- _executeHook('afterSanitizeShadowDOM', fragment, null);
1181
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
1299
1182
  };
1300
-
1301
- /**
1302
- * Sanitize
1303
- * Public method providing core sanitation functionality
1304
- *
1305
- * @param {String|Node} dirty string or DOM node
1306
- * @param {Object} cfg object
1307
- */
1308
1183
  // eslint-disable-next-line complexity
1309
1184
  DOMPurify.sanitize = function (dirty) {
1310
1185
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -1319,7 +1194,6 @@ function createDOMPurify() {
1319
1194
  if (IS_EMPTY_INPUT) {
1320
1195
  dirty = '<!-->';
1321
1196
  }
1322
-
1323
1197
  /* Stringify, in case dirty is an object */
1324
1198
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1325
1199
  if (typeof dirty.toString === 'function') {
@@ -1331,20 +1205,16 @@ function createDOMPurify() {
1331
1205
  throw typeErrorCreate('toString is not a function');
1332
1206
  }
1333
1207
  }
1334
-
1335
1208
  /* Return dirty HTML if DOMPurify cannot run */
1336
1209
  if (!DOMPurify.isSupported) {
1337
1210
  return dirty;
1338
1211
  }
1339
-
1340
1212
  /* Assign config vars */
1341
1213
  if (!SET_CONFIG) {
1342
1214
  _parseConfig(cfg);
1343
1215
  }
1344
-
1345
1216
  /* Clean up removed elements */
1346
1217
  DOMPurify.removed = [];
1347
-
1348
1218
  /* Check if dirty is correctly typed for IN_PLACE */
1349
1219
  if (typeof dirty === 'string') {
1350
1220
  IN_PLACE = false;
@@ -1378,45 +1248,34 @@ function createDOMPurify() {
1378
1248
  dirty.indexOf('<') === -1) {
1379
1249
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1380
1250
  }
1381
-
1382
1251
  /* Initialize the document to work on */
1383
1252
  body = _initDocument(dirty);
1384
-
1385
1253
  /* Check we have a DOM node from the data */
1386
1254
  if (!body) {
1387
1255
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1388
1256
  }
1389
1257
  }
1390
-
1391
1258
  /* Remove first element node (ours) if FORCE_BODY is set */
1392
1259
  if (body && FORCE_BODY) {
1393
1260
  _forceRemove(body.firstChild);
1394
1261
  }
1395
-
1396
1262
  /* Get node iterator */
1397
1263
  const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1398
-
1399
1264
  /* Now start iterating over the created document */
1400
1265
  while (currentNode = nodeIterator.nextNode()) {
1401
1266
  /* Sanitize tags and elements */
1402
- if (_sanitizeElements(currentNode)) {
1403
- continue;
1404
- }
1405
-
1267
+ _sanitizeElements(currentNode);
1268
+ /* Check attributes next */
1269
+ _sanitizeAttributes(currentNode);
1406
1270
  /* Shadow DOM detected, sanitize it */
1407
1271
  if (currentNode.content instanceof DocumentFragment) {
1408
1272
  _sanitizeShadowDOM(currentNode.content);
1409
1273
  }
1410
-
1411
- /* Check attributes, sanitize if necessary */
1412
- _sanitizeAttributes(currentNode);
1413
1274
  }
1414
-
1415
1275
  /* If we sanitized `dirty` in-place, return it. */
1416
1276
  if (IN_PLACE) {
1417
1277
  return dirty;
1418
1278
  }
1419
-
1420
1279
  /* Return sanitized string or DOM */
1421
1280
  if (RETURN_DOM) {
1422
1281
  if (RETURN_DOM_FRAGMENT) {
@@ -1441,12 +1300,10 @@ function createDOMPurify() {
1441
1300
  return returnNode;
1442
1301
  }
1443
1302
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1444
-
1445
1303
  /* Serialize doctype if allowed */
1446
1304
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1447
1305
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1448
1306
  }
1449
-
1450
1307
  /* Sanitize final string template-safe */
1451
1308
  if (SAFE_FOR_TEMPLATES) {
1452
1309
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1455,39 +1312,15 @@ function createDOMPurify() {
1455
1312
  }
1456
1313
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1457
1314
  };
1458
-
1459
- /**
1460
- * Public method to set the configuration once
1461
- * setConfig
1462
- *
1463
- * @param {Object} cfg configuration object
1464
- */
1465
1315
  DOMPurify.setConfig = function () {
1466
1316
  let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1467
1317
  _parseConfig(cfg);
1468
1318
  SET_CONFIG = true;
1469
1319
  };
1470
-
1471
- /**
1472
- * Public method to remove the configuration
1473
- * clearConfig
1474
- *
1475
- */
1476
1320
  DOMPurify.clearConfig = function () {
1477
1321
  CONFIG = null;
1478
1322
  SET_CONFIG = false;
1479
1323
  };
1480
-
1481
- /**
1482
- * Public method to check if an attribute value is valid.
1483
- * Uses last set config, if any. Otherwise, uses config defaults.
1484
- * isValidAttribute
1485
- *
1486
- * @param {String} tag Tag name of containing element.
1487
- * @param {String} attr Attribute name.
1488
- * @param {String} value Attribute value.
1489
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1490
- */
1491
1324
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1492
1325
  /* Initialize shared config vars if necessary. */
1493
1326
  if (!CONFIG) {
@@ -1497,54 +1330,24 @@ function createDOMPurify() {
1497
1330
  const lcName = transformCaseFunc(attr);
1498
1331
  return _isValidAttribute(lcTag, lcName, value);
1499
1332
  };
1500
-
1501
- /**
1502
- * AddHook
1503
- * Public method to add DOMPurify hooks
1504
- *
1505
- * @param {String} entryPoint entry point for the hook to add
1506
- * @param {Function} hookFunction function to execute
1507
- */
1508
1333
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1509
1334
  if (typeof hookFunction !== 'function') {
1510
1335
  return;
1511
1336
  }
1512
- hooks[entryPoint] = hooks[entryPoint] || [];
1513
1337
  arrayPush(hooks[entryPoint], hookFunction);
1514
1338
  };
1515
-
1516
- /**
1517
- * RemoveHook
1518
- * Public method to remove a DOMPurify hook at a given entryPoint
1519
- * (pops it from the stack of hooks if more are present)
1520
- *
1521
- * @param {String} entryPoint entry point for the hook to remove
1522
- * @return {Function} removed(popped) hook
1523
- */
1524
- DOMPurify.removeHook = function (entryPoint) {
1525
- if (hooks[entryPoint]) {
1526
- return arrayPop(hooks[entryPoint]);
1339
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
1340
+ if (hookFunction !== undefined) {
1341
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
1342
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
1527
1343
  }
1344
+ return arrayPop(hooks[entryPoint]);
1528
1345
  };
1529
-
1530
- /**
1531
- * RemoveHooks
1532
- * Public method to remove all DOMPurify hooks at a given entryPoint
1533
- *
1534
- * @param {String} entryPoint entry point for the hooks to remove
1535
- */
1536
1346
  DOMPurify.removeHooks = function (entryPoint) {
1537
- if (hooks[entryPoint]) {
1538
- hooks[entryPoint] = [];
1539
- }
1347
+ hooks[entryPoint] = [];
1540
1348
  };
1541
-
1542
- /**
1543
- * RemoveAllHooks
1544
- * Public method to remove all DOMPurify hooks
1545
- */
1546
1349
  DOMPurify.removeAllHooks = function () {
1547
- hooks = {};
1350
+ hooks = _createHooksMap();
1548
1351
  };
1549
1352
  return DOMPurify;
1550
1353
  }