content-security-toolkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +171 -0
  3. package/dist/config/default-extensions-config.json +103 -0
  4. package/dist/core/ContentProtector.d.ts +63 -0
  5. package/dist/core/ContentProtector.js +279 -0
  6. package/dist/core/index.d.ts +1 -0
  7. package/dist/core/index.js +2 -0
  8. package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
  9. package/dist/core/mediator/ContentProtectionMediator.js +238 -0
  10. package/dist/core/mediator/eventDataTypes.d.ts +205 -0
  11. package/dist/core/mediator/eventDataTypes.js +23 -0
  12. package/dist/core/mediator/handlers/abstractEventHandler.d.ts +67 -0
  13. package/dist/core/mediator/handlers/abstractEventHandler.js +106 -0
  14. package/dist/core/mediator/handlers/baseEventHandler.d.ts +65 -0
  15. package/dist/core/mediator/handlers/baseEventHandler.js +99 -0
  16. package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
  17. package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
  18. package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
  19. package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
  20. package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
  21. package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
  22. package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
  23. package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
  24. package/dist/core/mediator/handlers/index.d.ts +9 -0
  25. package/dist/core/mediator/handlers/index.js +34 -0
  26. package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
  27. package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
  28. package/dist/core/mediator/protection-event.d.ts +94 -0
  29. package/dist/core/mediator/protection-event.js +43 -0
  30. package/dist/core/mediator/types.d.ts +105 -0
  31. package/dist/core/mediator/types.js +1 -0
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.js +5 -0
  34. package/dist/strategies/AbstractStrategy.d.ts +152 -0
  35. package/dist/strategies/AbstractStrategy.js +296 -0
  36. package/dist/strategies/AbstractStrategy.mediator.d.ts +162 -0
  37. package/dist/strategies/AbstractStrategy.mediator.js +349 -0
  38. package/dist/strategies/ClipboardStrategy.d.ts +67 -0
  39. package/dist/strategies/ClipboardStrategy.js +291 -0
  40. package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
  41. package/dist/strategies/ContextMenuStrategy.js +454 -0
  42. package/dist/strategies/DevToolsStrategy copy.d.ts +85 -0
  43. package/dist/strategies/DevToolsStrategy copy.js +362 -0
  44. package/dist/strategies/DevToolsStrategy-detectorManager.d.ts +70 -0
  45. package/dist/strategies/DevToolsStrategy-detectorManager.js +309 -0
  46. package/dist/strategies/DevToolsStrategy-simple.d.ts +75 -0
  47. package/dist/strategies/DevToolsStrategy-simple.js +366 -0
  48. package/dist/strategies/DevToolsStrategy.d.ts +55 -0
  49. package/dist/strategies/DevToolsStrategy.js +314 -0
  50. package/dist/strategies/ExtensionStrategy.d.ts +66 -0
  51. package/dist/strategies/ExtensionStrategy.js +486 -0
  52. package/dist/strategies/IFrameStrategy.d.ts +49 -0
  53. package/dist/strategies/IFrameStrategy.js +255 -0
  54. package/dist/strategies/KeyboardStrategy.d.ts +35 -0
  55. package/dist/strategies/KeyboardStrategy.js +130 -0
  56. package/dist/strategies/PrintStrategy.d.ts +47 -0
  57. package/dist/strategies/PrintStrategy.js +201 -0
  58. package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
  59. package/dist/strategies/ScreenshotStrategy.js +488 -0
  60. package/dist/strategies/SelectionStrategy.d.ts +49 -0
  61. package/dist/strategies/SelectionStrategy.js +216 -0
  62. package/dist/strategies/StrategyRegistry.d.ts +133 -0
  63. package/dist/strategies/StrategyRegistry.js +379 -0
  64. package/dist/strategies/WatermarkStrategy.d.ts +47 -0
  65. package/dist/strategies/WatermarkStrategy.js +273 -0
  66. package/dist/strategies/index.d.ts +9 -0
  67. package/dist/strategies/index.js +10 -0
  68. package/dist/types/index.d.ts +271 -0
  69. package/dist/types/index.js +16 -0
  70. package/dist/utils/DOMObserver.d.ts +68 -0
  71. package/dist/utils/DOMObserver.js +134 -0
  72. package/dist/utils/base/LoggableComponent.d.ts +62 -0
  73. package/dist/utils/base/LoggableComponent.js +95 -0
  74. package/dist/utils/debuggerDetector/debuggerDetectionWorker.d.ts +6 -0
  75. package/dist/utils/debuggerDetector/debuggerDetectionWorker.js +24 -0
  76. package/dist/utils/debuggerDetector/debuggerDetector.d.ts +55 -0
  77. package/dist/utils/debuggerDetector/debuggerDetector.js +158 -0
  78. package/dist/utils/debuggerDetector/firefoxDetector.d.ts +8 -0
  79. package/dist/utils/debuggerDetector/firefoxDetector.js +64 -0
  80. package/dist/utils/detection.d.ts +29 -0
  81. package/dist/utils/detection.js +267 -0
  82. package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +105 -0
  83. package/dist/utils/detectors/AbstractDevToolsDetector.js +136 -0
  84. package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
  85. package/dist/utils/detectors/dateToStringDetector.js +96 -0
  86. package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
  87. package/dist/utils/detectors/debugLibDetector.js +195 -0
  88. package/dist/utils/detectors/debuggerDetectionWorker.d.ts +6 -0
  89. package/dist/utils/detectors/debuggerDetectionWorker.js +24 -0
  90. package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
  91. package/dist/utils/detectors/debuggerDetector.js +211 -0
  92. package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
  93. package/dist/utils/detectors/defineGetterDetector.js +150 -0
  94. package/dist/utils/detectors/detectorInterface.d.ts +36 -0
  95. package/dist/utils/detectors/detectorInterface.js +1 -0
  96. package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
  97. package/dist/utils/detectors/devToolsDetectorManager.js +246 -0
  98. package/dist/utils/detectors/firefoxDetector.d.ts +8 -0
  99. package/dist/utils/detectors/firefoxDetector.js +64 -0
  100. package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
  101. package/dist/utils/detectors/funcToStringDetector.js +90 -0
  102. package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
  103. package/dist/utils/detectors/regToStringDetector.js +129 -0
  104. package/dist/utils/detectors/sizeDetector.d.ts +54 -0
  105. package/dist/utils/detectors/sizeDetector.js +134 -0
  106. package/dist/utils/detectors/timingDetector.d.ts +55 -0
  107. package/dist/utils/detectors/timingDetector.js +143 -0
  108. package/dist/utils/dom.d.ts +20 -0
  109. package/dist/utils/dom.js +83 -0
  110. package/dist/utils/environment.d.ts +29 -0
  111. package/dist/utils/environment.js +267 -0
  112. package/dist/utils/eventManager.d.ts +167 -0
  113. package/dist/utils/eventManager.js +556 -0
  114. package/dist/utils/index.d.ts +2 -0
  115. package/dist/utils/index.js +3 -0
  116. package/dist/utils/intervalManager.d.ts +96 -0
  117. package/dist/utils/intervalManager.js +229 -0
  118. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
  119. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
  120. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
  121. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
  122. package/dist/utils/logging/LogLevel.d.ts +21 -0
  123. package/dist/utils/logging/LogLevel.js +46 -0
  124. package/dist/utils/logging/LoggingConfig.d.ts +68 -0
  125. package/dist/utils/logging/LoggingConfig.js +64 -0
  126. package/dist/utils/logging/LoggingFactory.d.ts +22 -0
  127. package/dist/utils/logging/LoggingFactory.js +61 -0
  128. package/dist/utils/logging/LoggingService.d.ts +235 -0
  129. package/dist/utils/logging/LoggingService.js +385 -0
  130. package/dist/utils/logging/SimpleLoggingService.d.ts +39 -0
  131. package/dist/utils/logging/SimpleLoggingService.js +58 -0
  132. package/dist/utils/logging/advanced/LogLevel.d.ts +21 -0
  133. package/dist/utils/logging/advanced/LogLevel.js +46 -0
  134. package/dist/utils/logging/advanced/LoggingConfig.d.ts +68 -0
  135. package/dist/utils/logging/advanced/LoggingConfig.js +64 -0
  136. package/dist/utils/logging/advanced/LoggingFactory.d.ts +22 -0
  137. package/dist/utils/logging/advanced/LoggingFactory.js +61 -0
  138. package/dist/utils/logging/advanced/LoggingService.d.ts +235 -0
  139. package/dist/utils/logging/advanced/LoggingService.js +385 -0
  140. package/dist/utils/logging/simple/Loggable.d.ts +33 -0
  141. package/dist/utils/logging/simple/Loggable.js +1 -0
  142. package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
  143. package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
  144. package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
  145. package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
  146. package/dist/utils/orientation.d.ts +15 -0
  147. package/dist/utils/orientation.js +32 -0
  148. package/dist/utils/protectedContentManager-simple.d.ts +86 -0
  149. package/dist/utils/protectedContentManager-simple.js +180 -0
  150. package/dist/utils/protectedContentManager.d.ts +162 -0
  151. package/dist/utils/protectedContentManager.js +427 -0
  152. package/dist/utils/screenshotDetector.d.ts +72 -0
  153. package/dist/utils/screenshotDetector.js +179 -0
  154. package/dist/utils/securityOverlayManager-observer-pause.d.ts +283 -0
  155. package/dist/utils/securityOverlayManager-observer-pause.js +878 -0
  156. package/dist/utils/securityOverlayManager-simple.d.ts +197 -0
  157. package/dist/utils/securityOverlayManager-simple.js +552 -0
  158. package/dist/utils/securityOverlayManager.d.ts +260 -0
  159. package/dist/utils/securityOverlayManager.js +774 -0
  160. package/dist/utils/timeoutManager.d.ts +55 -0
  161. package/dist/utils/timeoutManager.js +121 -0
  162. package/package.json +54 -0
@@ -0,0 +1,774 @@
1
+ import { DomObserver } from "./DOMObserver";
2
+ import { isBrowser } from "./environment";
3
+ import { ProtectionEventType } from "../core/mediator/protection-event";
4
+ import { isEventType } from "../core/mediator/eventDataTypes";
5
+ import { eventManager } from "./eventManager";
6
+ import { SimpleLoggingService } from "./logging/simple/SimpleLoggingService";
7
+ /**
8
+ * Utility class to manage security overlays
9
+ */
10
+ export class SecurityOverlayManager {
11
+ /**
12
+ * Create a new SecurityOverlayManager
13
+ * @param debugMode Enable debug mode for troubleshooting
14
+ */
15
+ constructor(debugMode = false) {
16
+ this.COMPONENT_NAME = "SecurityOverlayManager";
17
+ this.mediator = null;
18
+ this.domObserver = null;
19
+ // Main storage for overlays
20
+ this.overlays = new Map();
21
+ // Currently active/visible overlay
22
+ this.activeOverlayId = null;
23
+ // Queue of overlay IDs waiting to be shown
24
+ this.overlayQueue = [];
25
+ this.onElementsRemovedCallbacks = [];
26
+ this.debugMode = debugMode;
27
+ this.logger = new SimpleLoggingService(this.COMPONENT_NAME, debugMode);
28
+ this.logger.log("Initialized");
29
+ }
30
+ /**
31
+ * Set the mediator to communicate with the other components
32
+ * @param mediator The protection mediator
33
+ */
34
+ setMediator(mediator) {
35
+ this.mediator = mediator;
36
+ // Subscribe only to general overlay events
37
+ this.mediator.subscribe(ProtectionEventType.OVERLAY_SHOWN, this.handleOverlayShown.bind(this), {
38
+ context: this.COMPONENT_NAME,
39
+ });
40
+ this.mediator.subscribe(ProtectionEventType.OVERLAY_REMOVED, this.handleOverlayRemoved.bind(this), {
41
+ context: this.COMPONENT_NAME,
42
+ });
43
+ this.mediator.subscribe(ProtectionEventType.OVERLAY_RESTORED, this.handleOverlayRestored.bind(this), {
44
+ context: this.COMPONENT_NAME,
45
+ });
46
+ this.logger.log("Mediator set and subscriptions established");
47
+ }
48
+ /**
49
+ * Check if an overlay with the same owner and type already exists
50
+ * @param owner The owner to check
51
+ * @param overlayType The overlay type to check
52
+ * @returns True if a matching overlay exists
53
+ */
54
+ hasOverlayByOwnerAndType(owner, overlayType) {
55
+ for (const overlay of this.overlays.values()) {
56
+ if (overlay.owner === owner && overlay.overlayType === overlayType) {
57
+ return true;
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+ /**
63
+ * Handle overlay shown event
64
+ * @param event The protection event
65
+ */
66
+ handleOverlayShown(event) {
67
+ try {
68
+ this.logger.log(`Received overlay shown event from ${event.source}`, event.data);
69
+ // Use type guard for type-safe access
70
+ if (!isEventType(event, ProtectionEventType.OVERLAY_SHOWN)) {
71
+ this.logger.error("Received invalid event type for OVERLAY_SHOWN");
72
+ return;
73
+ }
74
+ const { data } = event;
75
+ if (!data || !data.options)
76
+ return;
77
+ // Check for duplicate registrations
78
+ if (this.hasOverlayByOwnerAndType(data.strategyName, data.overlayType)) {
79
+ this.logger.log(`Duplicate overlay registration detected for ${data.strategyName}/${data.overlayType}, removing existing overlay first`);
80
+ // Remove existing overlays with the same owner and type
81
+ for (const [overlayId, overlay] of this.overlays.entries()) {
82
+ if (overlay.owner === data.strategyName && overlay.overlayType === data.overlayType) {
83
+ this.removeOverlayById(overlayId);
84
+ break; // Only remove one to avoid potential issues
85
+ }
86
+ }
87
+ }
88
+ // Register and show the overlay
89
+ this.registerOverlay(data.strategyName, data.overlayType, data.options, data.priority || 0);
90
+ }
91
+ catch (error) {
92
+ this.logger.error("Error handling overlay shown event", error);
93
+ }
94
+ }
95
+ /**
96
+ * Handle overlay removed event
97
+ * @param event The protection event
98
+ */
99
+ handleOverlayRemoved(event) {
100
+ try {
101
+ this.logger.log(`Received overlay removed event from ${event.source}`, event.data);
102
+ // Use type guard for type-safe access
103
+ if (!isEventType(event, ProtectionEventType.OVERLAY_REMOVED)) {
104
+ this.logger.error("Received invalid event type for OVERLAY_REMOVED");
105
+ return;
106
+ }
107
+ const { data } = event;
108
+ if (!data)
109
+ return;
110
+ // Remove overlays by owner
111
+ this.removeOverlaysByOwner(data.strategyName);
112
+ }
113
+ catch (error) {
114
+ this.logger.error("Error handling overlay removed event", error);
115
+ }
116
+ }
117
+ /**
118
+ * Handle overlay restored event
119
+ * @param event The protection event
120
+ */
121
+ handleOverlayRestored(event) {
122
+ try {
123
+ this.logger.log(`Received overlay restored event from ${event.source}`, event.data);
124
+ const data = event.data;
125
+ if (!data)
126
+ return;
127
+ // Check and restore overlays for this owner
128
+ this.checkAndRestoreOverlaysByOwner(data.strategyName);
129
+ }
130
+ catch (error) {
131
+ this.logger.error("Error handling overlay restored event", error);
132
+ }
133
+ }
134
+ /**
135
+ * Register a new overlay
136
+ * @param owner The strategy or component that owns this overlay
137
+ * @param overlayType The type of overlay (e.g., "screenshot", "devtools")
138
+ * @param options Options for the overlay
139
+ * @param priority Priority for display order (higher displays on top)
140
+ * @returns The ID of the registered overlay
141
+ */
142
+ registerOverlay(owner, overlayType, options, priority = 0) {
143
+ if (!isBrowser())
144
+ return "";
145
+ // Generate a unique ID for the overlay
146
+ const overlayId = `overlay-${owner}-${overlayType}-${Date.now()}`;
147
+ // Create the stored overlay object
148
+ const storedOverlay = {
149
+ id: overlayId,
150
+ overlayType,
151
+ options: { ...options },
152
+ owner,
153
+ priority,
154
+ element: null,
155
+ blocker: null,
156
+ timeoutId: null,
157
+ isVisible: false,
158
+ createdAt: Date.now(),
159
+ };
160
+ // Store the overlay
161
+ this.overlays.set(overlayId, storedOverlay);
162
+ this.logger.log(`Registered overlay ${overlayId} for ${owner} (${overlayType})`);
163
+ // If no active overlay, show this one immediately
164
+ if (!this.activeOverlayId) {
165
+ this.showOverlayById(overlayId);
166
+ }
167
+ else {
168
+ // Otherwise, add to queue based on priority
169
+ this.addToQueue(overlayId);
170
+ // Check if this overlay should replace the current one based on priority
171
+ const activeOverlay = this.overlays.get(this.activeOverlayId);
172
+ const newOverlay = this.overlays.get(overlayId);
173
+ if (activeOverlay && newOverlay && newOverlay.priority > activeOverlay.priority) {
174
+ this.logger.log(`New overlay has higher priority, replacing active overlay`);
175
+ // Hide current overlay
176
+ this.hideOverlayById(this.activeOverlayId, false);
177
+ // Show new overlay
178
+ this.showOverlayById(overlayId);
179
+ }
180
+ }
181
+ return overlayId;
182
+ }
183
+ /**
184
+ * Add an overlay to the queue
185
+ * @param overlayId ID of the overlay to add to queue
186
+ */
187
+ addToQueue(overlayId) {
188
+ // Add to queue if not already in it
189
+ if (!this.overlayQueue.includes(overlayId)) {
190
+ this.overlayQueue.push(overlayId);
191
+ // Sort queue by priority (highest first)
192
+ this.overlayQueue.sort((a, b) => {
193
+ const overlayA = this.overlays.get(a);
194
+ const overlayB = this.overlays.get(b);
195
+ if (!overlayA || !overlayB)
196
+ return 0;
197
+ return overlayB.priority - overlayA.priority;
198
+ });
199
+ this.logger.log(`Added overlay ${overlayId} to queue, position ${this.overlayQueue.indexOf(overlayId) + 1}/${this.overlayQueue.length}`);
200
+ }
201
+ }
202
+ /**
203
+ * Show a specific overlay by ID
204
+ * @param overlayId ID of the overlay to show
205
+ * @returns True if the overlay was shown successfully
206
+ */
207
+ showOverlayById(overlayId) {
208
+ const overlay = this.overlays.get(overlayId);
209
+ if (!overlay)
210
+ return false;
211
+ this.logger.log(`Showing overlay ${overlayId} (${overlay.overlayType})`);
212
+ // Create the DOM elements
213
+ const result = this.createOverlayElements(overlay);
214
+ if (!result.overlay) {
215
+ this.logger.log(`Failed to create overlay elements for ${overlayId}`);
216
+ return false;
217
+ }
218
+ // Update the stored overlay with references to the elements
219
+ overlay.element = result.overlay;
220
+ overlay.blocker = result.blocker;
221
+ overlay.isVisible = true;
222
+ // Set as active overlay
223
+ this.activeOverlayId = overlayId;
224
+ // Remove from queue if it's in there
225
+ this.overlayQueue = this.overlayQueue.filter((id) => id !== overlayId);
226
+ // Set up auto-removal timeout if duration is specified
227
+ if (overlay.options.duration && overlay.options.duration > 0) {
228
+ overlay.timeoutId = window.setTimeout(() => {
229
+ this.removeOverlayById(overlayId);
230
+ overlay.timeoutId = null;
231
+ }, overlay.options.duration);
232
+ this.logger.log(`Overlay ${overlayId} will auto-remove after ${overlay.options.duration}ms`);
233
+ }
234
+ // Set up observer for auto-restoration if enabled
235
+ if (overlay.options.autoRestore !== false) {
236
+ this.setupObserver(overlay);
237
+ }
238
+ return true;
239
+ }
240
+ /**
241
+ * Hide a specific overlay by ID without removing it from storage
242
+ * @param overlayId ID of the overlay to hide
243
+ * @param processQueue Whether to process the queue after hiding
244
+ * @returns True if the overlay was hidden successfully
245
+ */
246
+ hideOverlayById(overlayId, processQueue = true) {
247
+ const overlay = this.overlays.get(overlayId);
248
+ if (!overlay || !overlay.isVisible)
249
+ return false;
250
+ this.logger.log(`Hiding overlay ${overlayId} (${overlay.overlayType})`);
251
+ // Clear any existing timeout
252
+ if (overlay.timeoutId !== null) {
253
+ window.clearTimeout(overlay.timeoutId);
254
+ overlay.timeoutId = null;
255
+ }
256
+ // Remove DOM elements
257
+ if (overlay.element && overlay.element.parentNode) {
258
+ overlay.element.parentNode.removeChild(overlay.element);
259
+ }
260
+ if (overlay.blocker && overlay.blocker.parentNode) {
261
+ overlay.blocker.parentNode.removeChild(overlay.blocker);
262
+ }
263
+ // Update state
264
+ overlay.element = null;
265
+ overlay.blocker = null;
266
+ overlay.isVisible = false;
267
+ // Clear active overlay reference if this was the active one
268
+ if (this.activeOverlayId === overlayId) {
269
+ this.activeOverlayId = null;
270
+ // Remove global event listeners when the active overlay is hidden
271
+ this.removeGlobalEventListeners();
272
+ // Process queue to show next overlay if requested
273
+ if (processQueue && this.overlayQueue.length > 0) {
274
+ const nextOverlayId = this.overlayQueue.shift();
275
+ if (nextOverlayId) {
276
+ this.showOverlayById(nextOverlayId);
277
+ }
278
+ }
279
+ }
280
+ return true;
281
+ }
282
+ /**
283
+ * Create overlay and blocker elements
284
+ * @param overlay The stored overlay information
285
+ * @returns Object containing the created elements
286
+ */
287
+ createOverlayElements(overlay) {
288
+ if (!isBrowser()) {
289
+ return { overlay: null, blocker: null };
290
+ }
291
+ // Create event blocker if requested
292
+ let blocker = null;
293
+ if (overlay.options.blockEvents) {
294
+ blocker = this.createEventBlocker();
295
+ document.body.appendChild(blocker);
296
+ this.logger.log(`Event blocker created for ${overlay.id}`);
297
+ }
298
+ // Create the overlay element
299
+ const overlayElement = this.createOverlay(overlay.options, overlay.owner);
300
+ document.body.appendChild(overlayElement);
301
+ this.logger.log(`Overlay element created for ${overlay.id}`);
302
+ return {
303
+ overlay: overlayElement,
304
+ blocker: blocker,
305
+ };
306
+ }
307
+ /**
308
+ * Remove a specific overlay by ID
309
+ * @param overlayId ID of the overlay to remove
310
+ * @returns True if the overlay was removed successfully
311
+ */
312
+ removeOverlayById(overlayId) {
313
+ const overlay = this.overlays.get(overlayId);
314
+ if (!overlay)
315
+ return false;
316
+ this.logger.log(`Removing overlay ${overlayId} (${overlay.overlayType})`);
317
+ // Check if this is the active overlay before hiding it
318
+ const isActive = this.activeOverlayId === overlayId;
319
+ // IMPORTANT: Stop the DOMObserver BEFORE removing DOM elements
320
+ // MutationObserver fires synchronously during DOM removal, so we must
321
+ // stop it first to prevent auto-restore from creating a new overlay
322
+ if (this.domObserver) {
323
+ this.domObserver.stopObserving();
324
+ this.domObserver = null;
325
+ this.logger.log(`Stopped DOM observer before removing overlay ${overlayId}`);
326
+ }
327
+ // Hide the overlay (removes DOM elements)
328
+ this.hideOverlayById(overlayId);
329
+ // If this WAS the active overlay, ensure global event listeners are removed
330
+ // (hideOverlayById should have already done this, but this is a safety net)
331
+ if (isActive) {
332
+ this.removeGlobalEventListeners();
333
+ }
334
+ // Remove from storage
335
+ this.overlays.delete(overlayId);
336
+ return true;
337
+ }
338
+ /**
339
+ * Remove all overlays for a specific owner
340
+ * @param owner The owner to remove overlays for
341
+ * @returns The number of overlays removed
342
+ */
343
+ removeOverlaysByOwner(owner) {
344
+ if (!isBrowser())
345
+ return 0;
346
+ let removedCount = 0;
347
+ const overlaysToRemove = [];
348
+ // Find all overlays owned by this owner
349
+ for (const [overlayId, overlay] of this.overlays.entries()) {
350
+ if (overlay.owner === owner) {
351
+ overlaysToRemove.push(overlayId);
352
+ }
353
+ }
354
+ // Remove each overlay
355
+ for (const overlayId of overlaysToRemove) {
356
+ if (this.removeOverlayById(overlayId)) {
357
+ removedCount++;
358
+ }
359
+ }
360
+ if (removedCount > 0) {
361
+ this.logger.log(`Removed ${removedCount} overlays for owner ${owner}`);
362
+ }
363
+ return removedCount;
364
+ }
365
+ /**
366
+ * Check and restore overlays for a specific owner
367
+ * @param owner The owner to check overlays for
368
+ * @returns The number of overlays restored
369
+ */
370
+ checkAndRestoreOverlaysByOwner(owner) {
371
+ if (!isBrowser())
372
+ return 0;
373
+ let restoredCount = 0;
374
+ // Find all overlays owned by this owner
375
+ for (const [overlayId, overlay] of this.overlays.entries()) {
376
+ if (overlay.owner === owner && overlay.options.autoRestore !== false && !overlay.isVisible) {
377
+ // If this overlay should be visible but isn't, restore it
378
+ if (this.activeOverlayId) {
379
+ // If there's already an active overlay, add this to the queue
380
+ this.addToQueue(overlayId);
381
+ }
382
+ else {
383
+ // Otherwise show it immediately
384
+ if (this.showOverlayById(overlayId)) {
385
+ restoredCount++;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ if (restoredCount > 0) {
391
+ this.logger.log(`Restored ${restoredCount} overlays for owner ${owner}`);
392
+ }
393
+ return restoredCount;
394
+ }
395
+ /**
396
+ * Create an overlay element
397
+ * @param options Options for the overlay
398
+ * @param owner The owner of the overlay (for data attribute)
399
+ * @returns The created overlay element
400
+ */
401
+ createOverlay(options, owner) {
402
+ if (!isBrowser()) {
403
+ throw new Error("Document is not available");
404
+ }
405
+ // Create overlay element
406
+ const overlay = document.createElement("div");
407
+ overlay.id = "security-overlay";
408
+ // Add data attribute for owner identification
409
+ overlay.setAttribute("data-owner", owner);
410
+ // Apply styles
411
+ const styles = {
412
+ position: "fixed",
413
+ top: "0",
414
+ left: "0",
415
+ width: "100%",
416
+ height: "100%",
417
+ backgroundColor: options.backgroundColor || "rgba(220, 38, 38, 0.9)",
418
+ zIndex: options.zIndex || "2147483647",
419
+ display: "flex",
420
+ flexDirection: "column",
421
+ justifyContent: "center",
422
+ alignItems: "center",
423
+ fontFamily: "sans-serif",
424
+ color: options.textColor || "white",
425
+ padding: "20px",
426
+ textAlign: "center",
427
+ pointerEvents: "auto", // Always allow interaction with the overlay itself
428
+ ...options.customStyles,
429
+ };
430
+ // Apply styles to the overlay
431
+ Object.entries(styles).forEach(([key, value]) => {
432
+ if (value !== undefined) {
433
+ overlay.style[key] = value;
434
+ }
435
+ });
436
+ // Create HTML content
437
+ let content = "";
438
+ if (options.title) {
439
+ content += `<h2 style="font-size: 24px; margin-bottom: 20px;">${options.title}</h2>`;
440
+ }
441
+ if (options.message) {
442
+ content += `<p style="font-size: 16px; margin-bottom: 10px;">${options.message}</p>`;
443
+ }
444
+ if (options.secondaryMessage) {
445
+ content += `<p style="font-size: 16px; margin-top: 10px;">${options.secondaryMessage}</p>`;
446
+ }
447
+ if (options.additionalContent) {
448
+ content += options.additionalContent;
449
+ }
450
+ if (options.showCloseButton) {
451
+ content += `
452
+ <button id="security-overlay-close" style="margin-top: 20px; padding: 10px 20px; background-color: white; color: black; border: none; border-radius: 4px; cursor: pointer; pointer-events: auto;">
453
+ ${options.closeButtonText || "Close"}
454
+ </button>
455
+ `;
456
+ }
457
+ overlay.innerHTML = content;
458
+ // Add event listener to close button if present
459
+ if (options.showCloseButton) {
460
+ setTimeout(() => {
461
+ const closeButton = document.getElementById("security-overlay-close");
462
+ if (closeButton) {
463
+ closeButton.addEventListener("click", () => {
464
+ if (options.onCloseButtonClick) {
465
+ options.onCloseButtonClick();
466
+ }
467
+ else {
468
+ // Find the overlay ID by owner and remove it
469
+ for (const [overlayId, storedOverlay] of this.overlays.entries()) {
470
+ if (storedOverlay.owner === owner && storedOverlay.element === overlay) {
471
+ this.removeOverlayById(overlayId);
472
+ break;
473
+ }
474
+ }
475
+ }
476
+ });
477
+ }
478
+ }, 0);
479
+ }
480
+ return overlay;
481
+ }
482
+ /**
483
+ * Create an event blocker that prevents interaction with the page
484
+ * @returns The created event blocker element
485
+ */
486
+ createEventBlocker() {
487
+ const blocker = document.createElement("div");
488
+ blocker.id = "security-event-blocker";
489
+ // Apply styles to make it cover the entire page and block all events
490
+ blocker.style.position = "fixed";
491
+ blocker.style.top = "0";
492
+ blocker.style.left = "0";
493
+ blocker.style.width = "100%";
494
+ blocker.style.height = "100%";
495
+ blocker.style.backgroundColor = "transparent"; // Transparent but will block events
496
+ blocker.style.zIndex = "2147483646"; // Just below the overlay
497
+ blocker.style.cursor = "not-allowed"; // Show not-allowed cursor
498
+ // Add event listeners to block all interactions
499
+ const blockEvent = (e) => {
500
+ e.preventDefault();
501
+ e.stopPropagation();
502
+ return false;
503
+ };
504
+ // Block all common events
505
+ const events = [
506
+ "click",
507
+ "dblclick",
508
+ "mousedown",
509
+ "mouseup",
510
+ "mousemove",
511
+ "touchstart",
512
+ "touchend",
513
+ "touchmove",
514
+ "touchcancel",
515
+ "keydown",
516
+ "keyup",
517
+ "keypress",
518
+ "contextmenu",
519
+ "selectstart",
520
+ "dragstart",
521
+ "wheel", // Add wheel event to block scrolling
522
+ "scroll", // Add scroll event as well
523
+ ];
524
+ events.forEach((eventType) => {
525
+ blocker.addEventListener(eventType, blockEvent, { capture: true, passive: false });
526
+ });
527
+ // Additional handling for wheel events on document and window
528
+ if (isBrowser()) {
529
+ // Use eventManager to register global event listeners
530
+ eventManager.addEventListener(document, "wheel", blockEvent, this.COMPONENT_NAME, {
531
+ capture: true,
532
+ passive: false,
533
+ priority: 10,
534
+ });
535
+ eventManager.addEventListener(window, "wheel", blockEvent, this.COMPONENT_NAME, {
536
+ capture: true,
537
+ passive: false,
538
+ priority: 10,
539
+ });
540
+ // Also prevent scrolling via touch on mobile
541
+ eventManager.addEventListener(document, "touchmove", blockEvent, this.COMPONENT_NAME, {
542
+ capture: true,
543
+ passive: false,
544
+ priority: 10,
545
+ });
546
+ eventManager.addEventListener(window, "touchmove", blockEvent, this.COMPONENT_NAME, {
547
+ capture: true,
548
+ passive: false,
549
+ priority: 10,
550
+ });
551
+ this.logger.log("Added global event listeners to prevent scrolling");
552
+ }
553
+ return blocker;
554
+ }
555
+ /**
556
+ * Set up DOM observer to detect when overlay elements are removed
557
+ * @param overlay The overlay to observe
558
+ */
559
+ setupObserver(overlay) {
560
+ if (!isBrowser() || !overlay.element)
561
+ return;
562
+ const elementsToWatch = [overlay.element];
563
+ if (overlay.blocker) {
564
+ elementsToWatch.push(overlay.blocker);
565
+ }
566
+ // Create a handler for element removal
567
+ const handleElementsRemoved = (removedElements) => {
568
+ this.logger.log(`Overlay elements removed from DOM for ${overlay.id}`, removedElements);
569
+ // Only restore if auto-restore is enabled and the overlay is still in our storage
570
+ const currentOverlay = this.overlays.get(overlay.id);
571
+ if (currentOverlay && currentOverlay.options.autoRestore !== false) {
572
+ this.logger.log(`Auto-restoring overlay ${overlay.id}`);
573
+ // Mark as not visible
574
+ currentOverlay.isVisible = false;
575
+ currentOverlay.element = null;
576
+ currentOverlay.blocker = null;
577
+ // If this was the active overlay, restore it immediately
578
+ if (this.activeOverlayId === overlay.id) {
579
+ this.showOverlayById(overlay.id);
580
+ }
581
+ else {
582
+ // Otherwise add to queue
583
+ this.addToQueue(overlay.id);
584
+ }
585
+ // Notify callbacks
586
+ this.notifyElementsRemovedCallbacks(removedElements);
587
+ }
588
+ };
589
+ // Stop any existing observer
590
+ if (this.domObserver) {
591
+ this.domObserver.stopObserving();
592
+ }
593
+ this.domObserver = new DomObserver({
594
+ targetElement: document.body,
595
+ elementsToWatch,
596
+ onElementsRemoved: handleElementsRemoved,
597
+ observeSubtree: true,
598
+ debugMode: this.debugMode,
599
+ name: "SecurityOverlayManager",
600
+ });
601
+ this.domObserver.startObserving();
602
+ this.logger.log(`DOM observer set up for overlay ${overlay.id}`);
603
+ }
604
+ /**
605
+ * Remove global event listeners that were added to document and window
606
+ */
607
+ removeGlobalEventListeners() {
608
+ if (!isBrowser())
609
+ return;
610
+ // Use eventManager to remove all events registered by this component
611
+ const removedCount = eventManager.removeEventsByOwner(this.COMPONENT_NAME);
612
+ this.logger.log(`Removed ${removedCount} global event listeners`);
613
+ // Re-enable scrolling on body
614
+ if (document.body) {
615
+ document.body.style.overflow = "";
616
+ document.body.style.position = "";
617
+ document.body.style.height = "";
618
+ document.body.style.width = "";
619
+ document.body.style.top = "";
620
+ document.body.style.left = "";
621
+ this.logger.log("Re-enabled scrolling on body");
622
+ }
623
+ // Re-enable scrolling on html
624
+ const htmlElement = document.documentElement;
625
+ if (htmlElement) {
626
+ htmlElement.style.overflow = "";
627
+ this.logger.log("Re-enabled scrolling on html");
628
+ }
629
+ }
630
+ /**
631
+ * Notify all callbacks when elements are removed
632
+ * @param removedElements The elements that were removed
633
+ */
634
+ notifyElementsRemovedCallbacks(removedElements) {
635
+ for (const callback of this.onElementsRemovedCallbacks) {
636
+ try {
637
+ callback(removedElements);
638
+ }
639
+ catch (error) {
640
+ this.logger.error("Error in elements removed callback:", error);
641
+ }
642
+ }
643
+ }
644
+ /**
645
+ * Add a callback to be called when overlay elements are removed
646
+ * @param callback Callback function
647
+ */
648
+ addElementsRemovedCallback(callback) {
649
+ this.onElementsRemovedCallbacks.push(callback);
650
+ }
651
+ /**
652
+ * Remove a callback
653
+ * @param callback Callback function to remove
654
+ */
655
+ removeElementsRemovedCallback(callback) {
656
+ this.onElementsRemovedCallbacks = this.onElementsRemovedCallbacks.filter((cb) => cb !== callback);
657
+ }
658
+ /**
659
+ * Get all overlays for a specific owner
660
+ * @param owner The owner to get overlays for
661
+ * @returns Array of overlay IDs
662
+ */
663
+ getOverlaysByOwner(owner) {
664
+ const overlayIds = [];
665
+ for (const [overlayId, overlay] of this.overlays.entries()) {
666
+ if (overlay.owner === owner) {
667
+ overlayIds.push(overlayId);
668
+ }
669
+ }
670
+ return overlayIds;
671
+ }
672
+ /**
673
+ * Get all active overlays
674
+ * @returns Array of active overlay IDs
675
+ */
676
+ getActiveOverlays() {
677
+ const activeOverlays = [];
678
+ for (const [overlayId, overlay] of this.overlays.entries()) {
679
+ if (overlay.isVisible) {
680
+ activeOverlays.push(overlayId);
681
+ }
682
+ }
683
+ return activeOverlays;
684
+ }
685
+ /**
686
+ * Check if an overlay exists
687
+ * @param overlayId The overlay ID
688
+ * @returns True if the overlay exists
689
+ */
690
+ hasOverlay(overlayId) {
691
+ return this.overlays.has(overlayId);
692
+ }
693
+ /**
694
+ * Get the currently active overlay ID
695
+ * @returns The active overlay ID or null if none is active
696
+ */
697
+ getActiveOverlayId() {
698
+ return this.activeOverlayId;
699
+ }
700
+ /**
701
+ * Get the overlay queue
702
+ * @returns Array of overlay IDs in the queue
703
+ */
704
+ getOverlayQueue() {
705
+ return [...this.overlayQueue];
706
+ }
707
+ /**
708
+ * Clear all overlays
709
+ * @returns The number of overlays removed
710
+ */
711
+ clearAllOverlays() {
712
+ if (!isBrowser())
713
+ return 0;
714
+ const overlayIds = Array.from(this.overlays.keys());
715
+ let removedCount = 0;
716
+ for (const overlayId of overlayIds) {
717
+ if (this.removeOverlayById(overlayId)) {
718
+ removedCount++;
719
+ }
720
+ }
721
+ // Clear queue
722
+ this.overlayQueue = [];
723
+ this.activeOverlayId = null;
724
+ // Remove global event listeners
725
+ this.removeGlobalEventListeners();
726
+ if (removedCount > 0) {
727
+ this.logger.log(`Cleared all ${removedCount} overlays`);
728
+ }
729
+ return removedCount;
730
+ }
731
+ /**
732
+ * Set debug mode
733
+ * @param enabled Whether debug mode should be enabled
734
+ */
735
+ setDebugMode(enabled) {
736
+ this.debugMode = enabled;
737
+ this.logger.setDebugMode(enabled);
738
+ this.logger.log(`Debug mode ${enabled ? "enabled" : "disabled"}`);
739
+ }
740
+ /**
741
+ * Get debug information about registered overlays
742
+ * @returns Object with debug information
743
+ */
744
+ getDebugInfo() {
745
+ const overlaysByOwner = {};
746
+ const overlaysByType = {};
747
+ let totalOverlays = 0;
748
+ const overlayDetails = [];
749
+ for (const [overlayId, overlay] of this.overlays.entries()) {
750
+ totalOverlays++;
751
+ // Count by owner
752
+ overlaysByOwner[overlay.owner] = (overlaysByOwner[overlay.owner] || 0) + 1;
753
+ // Count by type
754
+ overlaysByType[overlay.overlayType] = (overlaysByType[overlay.overlayType] || 0) + 1;
755
+ // Add detailed overlay info
756
+ overlayDetails.push({
757
+ id: overlayId,
758
+ owner: overlay.owner,
759
+ type: overlay.overlayType,
760
+ isVisible: overlay.isVisible,
761
+ priority: overlay.priority,
762
+ createdAt: overlay.createdAt,
763
+ });
764
+ }
765
+ return {
766
+ totalOverlays,
767
+ overlaysByOwner,
768
+ overlaysByType,
769
+ activeOverlayId: this.activeOverlayId,
770
+ queueLength: this.overlayQueue.length,
771
+ overlayDetails,
772
+ };
773
+ }
774
+ }