"""Service for applying browser protection during automation.""" from typing import Optional from playwright.sync_api import Page from domain.value_objects.browser_protection_style import BrowserProtectionStyle, ProtectionLevel class BrowserProtectionService: """Handles browser protection during automation to prevent user interference.""" SHIELD_ELEMENT_ID = "accountforge-shield" PROTECTION_STYLE_ID = "accountforge-protection-styles" def protect_browser(self, page: Page, style: BrowserProtectionStyle) -> None: """Apply protection to the browser page based on the configured style.""" if style.level == ProtectionLevel.NONE: return # Generate and inject protection script script = self._generate_protection_script(style) page.evaluate(script) # Speichere Script für Wiederanwendung escaped_script = script.replace('`', '\\`') page.evaluate(f""" window.__accountforge_protection = `{escaped_script}`; """) def remove_protection(self, page: Page) -> None: """Remove all protection from the browser page.""" page.evaluate(f""" // Remove shield element const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}'); if (shield) shield.remove(); // Remove style element const styles = document.getElementById('{self.PROTECTION_STYLE_ID}'); if (styles) styles.remove(); // Note: Event listeners will be removed on page navigation """) def _generate_protection_script(self, style: BrowserProtectionStyle) -> str: """Generate JavaScript code for protection based on style configuration.""" script_parts = [] # Create shield overlay if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]: script_parts.append(self._create_shield_script(style)) # Add visual effects if style.show_border or (style.level == ProtectionLevel.LIGHT): script_parts.append(self._create_visual_effects_script(style)) # Block interactions if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]: script_parts.append(self._create_interaction_blocker_script()) return "\n".join(script_parts) def _create_shield_script(self, style: BrowserProtectionStyle) -> str: """Create the main shield overlay element.""" badge_positions = { "top-left": "top: 20px; left: 20px;", "top-right": "top: 20px; right: 20px;", "bottom-left": "bottom: 20px; left: 20px;", "bottom-right": "bottom: 20px; right: 20px;" } badge_position_css = badge_positions.get(style.badge_position, badge_positions["top-right"]) blur_css = "backdrop-filter: blur(2px);" if style.blur_effect else "" return f""" // Create shield overlay const shield = document.createElement('div'); shield.id = '{self.SHIELD_ELEMENT_ID}'; shield.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: {style.get_overlay_color()}; {blur_css} z-index: 2147483647; cursor: not-allowed; user-select: none; pointer-events: all; `; // Add info badge if configured if ({str(style.show_badge).lower()}) {{ const badge = document.createElement('div'); badge.style.cssText = ` position: absolute; {badge_position_css} background: rgba(220, 38, 38, 0.95); color: white; padding: 12px 24px; border-radius: 8px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.3); animation: fadeIn 0.3s ease-in; `; badge.textContent = '{style.badge_text}'; shield.appendChild(badge); }} document.documentElement.appendChild(shield); """ def _create_visual_effects_script(self, style: BrowserProtectionStyle) -> str: """Create visual effects like animated border.""" return f""" // Add styles for visual effects const styleElement = document.createElement('style'); styleElement.id = '{self.PROTECTION_STYLE_ID}'; styleElement.textContent = ` @keyframes pulse {{ 0% {{ opacity: 0.4; }} 50% {{ opacity: 0.8; }} 100% {{ opacity: 0.4; }} }} @keyframes fadeIn {{ from {{ opacity: 0; transform: translateY(-10px); }} to {{ opacity: 1; transform: translateY(0); }} }} {self._get_border_css(style) if style.show_border else ''} `; document.head.appendChild(styleElement); {self._get_border_element_script(style) if style.show_border else ''} """ def _get_border_css(self, style: BrowserProtectionStyle) -> str: """Get CSS for animated border.""" return f""" #accountforge-border {{ position: fixed; top: 0; left: 0; width: 100%; height: 100%; border: 3px solid {style.border_color}; box-shadow: inset 0 0 30px rgba(255, 0, 0, 0.2); pointer-events: none; animation: pulse 2s infinite; z-index: 2147483646; }} """ def _get_border_element_script(self, style: BrowserProtectionStyle) -> str: """Get script to create border element.""" return f""" // Create animated border const border = document.createElement('div'); border.id = 'accountforge-border'; document.body.appendChild(border); """ def _create_interaction_blocker_script(self) -> str: """Create script to block all user interactions.""" return f""" // Verhindere das Shield selbst entfernt zu werden (function() {{ // Prüfe ob Shield schon existiert if (document.getElementById('{self.SHIELD_ELEMENT_ID}')) {{ return; }} // Block all interaction events const blockedEvents = [ 'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keypress', 'keyup', 'touchstart', 'touchend', 'touchmove', 'contextmenu', 'wheel', 'scroll', 'input', 'change', 'focus' ]; const eventBlocker = function(e) {{ // Prüfe ob das Event vom Shield selbst kommt const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}'); if (shield && (e.target === shield || shield.contains(e.target))) {{ return; }} e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); return false; }}; // Add event listeners with capture phase blockedEvents.forEach(eventType => {{ document.addEventListener(eventType, eventBlocker, true); window.addEventListener(eventType, eventBlocker, true); }}); // Disable text selection document.body.style.userSelect = 'none'; document.body.style.webkitUserSelect = 'none'; document.body.style.mozUserSelect = 'none'; document.body.style.msUserSelect = 'none'; // Disable all input fields const disableInputs = () => {{ document.querySelectorAll('input, textarea, select, button').forEach(el => {{ el.style.pointerEvents = 'none'; el.setAttribute('disabled', 'true'); el.setAttribute('readonly', 'true'); }}); }}; // Initial disable disableInputs(); // Re-disable on DOM changes const inputObserver = new MutationObserver(disableInputs); inputObserver.observe(document.body, {{ childList: true, subtree: true }}); // Disable drag document.ondragstart = function() {{ return false; }}; document.onselectstart = function() {{ return false; }}; // Prevent focus on any element document.addEventListener('focusin', function(e) {{ e.target.blur(); }}, true); }})(); """