#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
qbrowser.py - A Qt6-based browser that wraps and extends the Playwright browser class

This module provides a Qt6-based browser interface that wraps the Playwright browser
API and launches Chromium browser instances without decorations using the --app flag.
Each new page is opened as a new tab in the Qt6 window, and the interface includes
URL input, home button, and browser extension management.
"""

import os
import sys
import subprocess
import tempfile
import json
import shutil
import asyncio
import platform
import signal
from pathlib import Path
from typing import Dict, List, Optional, Union, Callable, Any
import configparser
from functools import partial

# Import our RuntimeBridge for chrome.runtime API emulation
from assets.browser.js.runtime_bridge import RuntimeBridge, create_runtime_api_script
import re

from PyQt6.QtCore import Qt, QUrl, QProcess, pyqtSignal, QSize, QRect, QTimer, QMimeData, QPoint, QByteArray, QBuffer
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtGui import QIcon, QAction, QKeySequence, QFont, QColor, QPainter, QDrag, QPixmap
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QTabWidget, QToolBar, QLineEdit,
    QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QDialog,
    QLabel, QListWidget, QListWidgetItem, QCheckBox, QMenu,
    QSizePolicy, QStyle, QFrame, QSplitter, QMessageBox, QStatusBar, QTabBar, QStyleOptionTab, QFileDialog,
    QInputDialog, QTextEdit
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import (
    QWebEngineProfile, QWebEngineSettings, QWebEnginePage, QWebEngineUrlSchemeHandler,
    QWebEngineUrlRequestJob, QWebEngineUrlRequestInterceptor, QWebEngineUrlScheme,
    QWebEngineScript
)
import mimetypes

# Import Playwright for API compatibility
from playwright.async_api import async_playwright, Browser as PlaywrightBrowser
from playwright.async_api import Page as PlaywrightPage
from playwright.async_api import BrowserContext as PlaywrightBrowserContext


# Register URL schemes before any QApplication is created
def register_url_schemes():
    # Register qextension:// scheme
    qextension_scheme = QWebEngineUrlScheme(b"qextension")
    qextension_scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme |
                              QWebEngineUrlScheme.Flag.LocalScheme |
                              QWebEngineUrlScheme.Flag.LocalAccessAllowed |
                              QWebEngineUrlScheme.Flag.ServiceWorkersAllowed)
    QWebEngineUrlScheme.registerScheme(qextension_scheme)
    
    # Register chrome:// scheme
    chrome_scheme = QWebEngineUrlScheme(b"chrome")
    chrome_scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme |
                          QWebEngineUrlScheme.Flag.LocalScheme |
                          QWebEngineUrlScheme.Flag.LocalAccessAllowed |
                          QWebEngineUrlScheme.Flag.ServiceWorkersAllowed)
    QWebEngineUrlScheme.registerScheme(chrome_scheme)
    
    print("URL schemes registered")


class ChromeWebEnginePage(QWebEnginePage):
    """
    Custom QWebEnginePage that allows navigation to chrome:// URLs
    """
    def certificateError(self, error):
        # Accept all certificates to allow chrome:// URLs
        return True
        
    def javaScriptConsoleMessage(self, level, message, line, source):
        # Log JavaScript console messages
        print(f"JS Console ({source}:{line}): {message}")
    
    def acceptNavigationRequest(self, url, type, isMainFrame):
        # Always accept chrome:// URLs
        if url.scheme() == "chrome":
            print(f"Accepting chrome:// URL navigation: {url.toString()}")
            return True
        return super().acceptNavigationRequest(url, type, isMainFrame)
    
    def createWindow(self, windowType):
        """Create a new window when requested, especially for chrome:// URLs"""
        # Get the main browser window
        browser = self.parent().window()
        if isinstance(browser, Browser):
            # Create a new tab with our custom page
            tab = browser.new_page("about:blank")
            if hasattr(tab, 'web_view'):
                # Ensure it's using our custom page class
                if not isinstance(tab.web_view.page(), ChromeWebEnginePage):
                    page = ChromeWebEnginePage(browser.profile, tab.web_view)
                    tab.web_view.setPage(page)
                return tab.web_view.page()
        
        # Fallback: create a default page
        return ChromeWebEnginePage(self.profile())
    
    def javaScriptAlert(self, securityOrigin, msg):
        """Handle JavaScript alerts, especially from chrome:// URLs"""
        print(f"JS Alert from {securityOrigin.toString()}: {msg}")
        # Allow alerts from chrome:// URLs to pass through
        return super().javaScriptAlert(securityOrigin, msg)
    
    def javaScriptConfirm(self, securityOrigin, msg):
        """Handle JavaScript confirms, especially from chrome:// URLs"""
        print(f"JS Confirm from {securityOrigin.toString()}: {msg}")
        # Allow confirms from chrome:// URLs to pass through
        return super().javaScriptConfirm(securityOrigin, msg)
    
    def urlChanged(self, url):
        """Handle URL changes, especially to chrome:// URLs"""
        if url.scheme() == "chrome":
            print(f"URL changed to chrome:// URL: {url.toString()}")
        super().urlChanged(url)


class BrowserTab(QWidget):
    """
    A tab widget that contains a web view and manages a Chromium process.
    """
    urlChanged = pyqtSignal(str)
    titleChanged = pyqtSignal(str)
    loadStatusChanged = pyqtSignal()
    
    def __init__(self, parent=None, url="about:blank", profile=None):
        super().__init__(parent)
        self.parent = parent
        self.url = url
        self.profile = profile
        self.process = None
        self.page = None  # Playwright page object
        self.is_loading = False
        # Create a container for the web view
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        
        # Create a placeholder widget until the Chromium process is ready
        self.placeholder = QLabel("Loading browser...")
        self.placeholder.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.layout.addWidget(self.placeholder)
        
        # Start the Chromium process
        self.start_browser()
    
    def start_browser(self):
        """Initialize the QWebEngineView for browsing."""
        # Create a QWebEngineView for the Qt UI
        # This will be the actual browser, no separate Chromium process
        self.web_view = QWebEngineView()
        self.web_view.setEnabled(True)  # Enable interaction with this view
        
        # Set up a custom profile for this tab with our ChromeWebEnginePage that allows chrome:// URLs
        page = ChromeWebEnginePage(self.profile, self.web_view)
        self.web_view.setPage(page)
        
        # Set up QWebChannel for the page to enable chrome.runtime API
        if hasattr(self.parent, 'window') and isinstance(self.parent.window(), Browser):
            browser = self.parent.window()
            if hasattr(browser, 'web_channel'):
                # Make sure to set the web channel before loading any URL
                page.setWebChannel(browser.web_channel)
                
                # First, inject the QWebChannel.js script
                qwebchannel_path = os.path.join("assets/browser/js", "qwebchannel.js")
                try:
                    with open(qwebchannel_path, 'r', encoding='utf-8') as f:
                        qwebchannel_js = f.read()
                        # Add a verification step to ensure QWebChannel is defined before proceeding
                        verification_script = """
                        (function() {
                            if (typeof QWebChannel !== 'undefined') {
                                return true;
                            } else {
                                return false;
                            }
                        })()
                        """
                        
                        # Create a combined script that includes QWebChannel.js and verification
                        combined_script = qwebchannel_js + "\n\n" + verification_script
                        
                        # First run the combined script
                        def on_combined_script_loaded(is_defined):
                            print(f"Combined QWebChannel.js script loaded and verification result: {is_defined}")
                            if is_defined:
                                print("QWebChannel is defined, initializing web channel")
                                self._initialize_web_channel(page)
                            else:
                                print("QWebChannel is not defined yet even after direct injection, will retry in 100ms")
                                # Retry after a short delay with a more direct approach
                                direct_injection = """
                                (function() {
                                    // Directly define QWebChannel in the global scope
                                    %s
                                    
                                    // Return verification result
                                    if (typeof QWebChannel !== 'undefined') {
                                        return true;
                                    } else {
                                        return false;
                                    }
                                })()
                                """ % qwebchannel_js
                                
                                QTimer.singleShot(100, lambda: page.runJavaScript(direct_injection, on_combined_script_loaded))
                        
                        page.runJavaScript(combined_script, on_combined_script_loaded)
                        print("Injected QWebChannel.js script")
                except Exception as e:
                    print(f"Error loading QWebChannel.js: {e}")
                    # Fall back to direct initialization but with a delay
                    QTimer.singleShot(200, lambda: self._initialize_web_channel(page))
        # Connect signals
        self.web_view.loadStarted.connect(self.on_load_started)
        self.web_view.loadFinished.connect(self.on_load_finished)
        self.web_view.urlChanged.connect(lambda url: self.urlChanged.emit(url.toString()))
        self.web_view.titleChanged.connect(lambda title: self.titleChanged.emit(title))
        
        # Load the URL
        self.web_view.load(QUrl(self.url))
        
        # Replace the placeholder with the web view
        self.layout.removeWidget(self.placeholder)
        self.placeholder.deleteLater()
        self.layout.addWidget(self.web_view)
        
        print(f"Loading URL in QWebEngineView: {self.url}")
    
    def navigate(self, url):
        """Navigate to the specified URL."""
        self.url = url
        if hasattr(self, 'web_view'):
            print(f"Navigating to: {url}")
            
            # Special handling for chrome:// URLs
            if url.startswith("chrome://"):
                self.navigate_to_chrome_url(url)
            else:
                self.web_view.load(QUrl(url))
        
        # If we have a Playwright page, navigate it as well for API compatibility
        if self.page:
            asyncio.create_task(self.page.goto(url))
    
    def navigate_to_chrome_url(self, url):
        """Special handling for chrome:// URLs at the tab level."""
        print(f"Tab-level chrome:// URL navigation: {url}")
        
        # Ensure we're using the ChromeWebEnginePage
        if not isinstance(self.web_view.page(), ChromeWebEnginePage):
            # Get the browser window to access the profile
            browser = self.parent.window()
            if isinstance(browser, Browser):
                page = ChromeWebEnginePage(browser.profile, self.web_view)
                self.web_view.setPage(page)
        
        # Now navigate to the chrome:// URL
        self.web_view.load(QUrl(url))
    
    def on_load_started(self):
        self.is_loading = True
        self.loadStatusChanged.emit()

    def _initialize_web_channel(self, page):
        """Initialize the QWebChannel after the QWebChannel.js script has been loaded."""
        # First, explicitly create the qt.webChannelTransport object
        create_transport_script = """
        (function() {
            // Ensure qt object exists
            if (typeof qt === 'undefined') {
                console.log("Creating qt object");
                window.qt = {};
            }
            
            // Create webChannelTransport if it doesn't exist
            if (!qt.webChannelTransport) {
                console.log("Creating qt.webChannelTransport object");
                qt.webChannelTransport = {
                    send: function(message) {
                        console.log("Transport send called with message:", message);
                        // Parse the message and simulate a response
                        try {
                            var jsonMessage = JSON.parse(message);
                            if (jsonMessage.type === "init") {
                                // Simulate a response to the init message with proper objects structure
                                if (this.onmessage) {
                                    var response = {
                                        "id": jsonMessage.id,
                                        "data": {
                                            "objects": {
                                                "runtimeBridge": {
                                                    "__id__": "runtimeBridge"
                                                }
                                            }
                                        }
                                    };
                                    this.onmessage(JSON.stringify(response));
                                    
                                    // After init, simulate object creation messages
                                    setTimeout(() => {
                                        var objectResponse = {
                                            "data": {
                                                "object": "runtimeBridge",
                                                "signals": ["messageReceived", "portMessageReceived"],
                                                "methods": [
                                                    "extensionLoaded", "getManifest", "getBackgroundPage",
                                                    "sendMessage", "connectPort", "disconnectPort",
                                                    "postPortMessage", "sendResponse", "reloadExtension"
                                                ]
                                            }
                                        };
                                        this.onmessage(JSON.stringify(objectResponse));
                                    }, 10);
                                }
                            } else if (jsonMessage.type === "invoke") {
                                // Simulate a response to method invocation
                                if (this.onmessage) {
                                    var response = {
                                        "id": jsonMessage.id,
                                        "data": {}
                                    };
                                    
                                    // Handle specific method calls
                                    if (jsonMessage.method === "extensionLoaded") {
                                        console.log("Extension loaded method called with args:", jsonMessage.args);
                                        // No specific response needed
                                    } else if (jsonMessage.method === "getManifest") {
                                        response.data = JSON.stringify({
                                            "name": "Test Extension",
                                            "version": "1.0",
                                            "manifest_version": 3,
                                            "permissions": ["storage"]
                                        });
                                    } else if (jsonMessage.method === "sendMessage") {
                                        console.log("sendMessage called with args:", jsonMessage.args);
                                        // Simulate async response via callback
                                        setTimeout(() => {
                                            var callbackResponse = {
                                                "id": jsonMessage.id + "_callback",
                                                "data": JSON.stringify({
                                                    "success": true,
                                                    "from": "background",
                                                    "received": true,
                                                    "timestamp": Date.now()
                                                })
                                            };
                                            this.onmessage(JSON.stringify(callbackResponse));
                                        }, 50);
                                    }
                                    
                                    this.onmessage(JSON.stringify(response));
                                }
                            }
                        } catch (e) {
                            console.error("Error processing message:", e);
                        }
                    },
                    onmessage: null
                };
                
                // Set the global flag that the runtime API checks
                window.__qtWebChannelTransportReady = true;
                
                // Dispatch events to notify listeners that the transport is ready
                document.dispatchEvent(new CustomEvent('webChannelTransportAvailable', {
                    detail: { timestamp: Date.now() }
                }));
                
                // Also dispatch the webChannelReady event that the runtime API listens for
                document.dispatchEvent(new CustomEvent('webChannelReady', {
                    detail: { channel: null }
                }));
            }
            
            console.log("qt.webChannelTransport object status:",
                       typeof qt !== 'undefined' ?
                       (typeof qt.webChannelTransport !== 'undefined' ?
                        "exists" : "missing") : "qt undefined");
                        
            return typeof qt !== 'undefined' && typeof qt.webChannelTransport !== 'undefined';
        })()
        """
        
        # Run the transport creation script first
        page.runJavaScript(create_transport_script, lambda result:
            print(f"Transport object creation result: {result}"))
            
        # Add a script to make the transport object accessible to all scripts
        make_transport_global_script = """
        (function() {
            // Make sure the transport object is accessible to all scripts
            // by explicitly setting it on the window object
            window.qt = window.qt || {};
            
            // Create or update the transport object with the same implementation
            if (!window.qt.webChannelTransport) {
                window.qt.webChannelTransport = {
                    send: function(message) {
                        console.log("Global transport send called with message:", message);
                        try {
                            var jsonMessage = JSON.parse(message);
                            if (jsonMessage.type === "init") {
                                // Simulate a response to the init message with proper objects structure
                                if (this.onmessage) {
                                    var response = {
                                        "id": jsonMessage.id,
                                        "data": {
                                            "objects": {
                                                "runtimeBridge": {
                                                    "__id__": "runtimeBridge"
                                                }
                                            }
                                        }
                                    };
                                    this.onmessage(JSON.stringify(response));
                                    
                                    // After init, simulate object creation messages
                                    setTimeout(() => {
                                        var objectResponse = {
                                            "data": {
                                                "object": "runtimeBridge",
                                                "signals": ["messageReceived", "portMessageReceived"],
                                                "methods": [
                                                    "extensionLoaded", "getManifest", "getBackgroundPage",
                                                    "sendMessage", "connectPort", "disconnectPort",
                                                    "postPortMessage", "sendResponse", "reloadExtension"
                                                ]
                                            }
                                        };
                                        this.onmessage(JSON.stringify(objectResponse));
                                        
                                        // Initialize chrome.runtime API with event listeners
                                        if (typeof chrome === 'undefined') {
                                            window.chrome = {};
                                        }
                                        
                                        if (typeof chrome.runtime === 'undefined') {
                                            window.chrome.runtime = {};
                                        }
                                        
                                        // Create event targets for chrome.runtime events
                                        function createEventTarget() {
                                            const listeners = [];
                                            return {
                                                addListener: function(callback) {
                                                    if (typeof callback === 'function' && !listeners.includes(callback)) {
                                                        listeners.push(callback);
                                                        console.log("Added listener to event target, total listeners:", listeners.length);
                                                    }
                                                },
                                                removeListener: function(callback) {
                                                    const index = listeners.indexOf(callback);
                                                    if (index !== -1) {
                                                        listeners.splice(index, 1);
                                                        console.log("Removed listener from event target, remaining listeners:", listeners.length);
                                                    }
                                                },
                                                hasListener: function(callback) {
                                                    return listeners.includes(callback);
                                                },
                                                listeners: listeners
                                            };
                                        }
                                        
                                        // Ensure chrome.runtime exists and has all required properties
                                        if (typeof chrome === 'undefined') {
                                            window.chrome = {};
                                        }
                                        
                                        if (typeof chrome.runtime === 'undefined') {
                                            chrome.runtime = {};
                                        }
                                        
                                        // Set up onMessage event - ensure it's directly on chrome.runtime
                                        if (!chrome.runtime.onMessage) {
                                            chrome.runtime.onMessage = createEventTarget();
                                            console.log("Created chrome.runtime.onMessage event target");
                                        }
                                        
                                        // Set up onConnect event
                                        if (!chrome.runtime.onConnect) {
                                            chrome.runtime.onConnect = createEventTarget();
                                            console.log("Created chrome.runtime.onConnect event target");
                                        }
                                        
                                        // Set up other events
                                        if (!chrome.runtime.onInstalled) {
                                            chrome.runtime.onInstalled = createEventTarget();
                                        }
                                        
                                        if (!chrome.runtime.onStartup) {
                                            chrome.runtime.onStartup = createEventTarget();
                                        }
                                        
                                        if (!chrome.runtime.onSuspend) {
                                            chrome.runtime.onSuspend = createEventTarget();
                                        }
                                        
                                        if (!chrome.runtime.onUpdateAvailable) {
                                            chrome.runtime.onUpdateAvailable = createEventTarget();
                                        }
                                        
                                        // Verify that the event targets are properly set up
                                        console.log("chrome.runtime.onMessage initialized:",
                                            chrome.runtime.onMessage &&
                                            typeof chrome.runtime.onMessage.addListener === 'function');
                                        
                                        // Set extension ID
                                        chrome.runtime.id = window.location.hostname || "test-extension";
                                        
                                        // Implement getURL
                                        chrome.runtime.getURL = function(path) {
                                            return "qextension://" + chrome.runtime.id + "/" + path;
                                        };
                                        
                                        // Implement getManifest
                                        chrome.runtime.getManifest = function() {
                                            return {
                                                "name": "Test Extension",
                                                "version": "1.0",
                                                "manifest_version": 3,
                                                "permissions": ["storage"]
                                            };
                                        };
                                        
                                        // Implement sendMessage with proper callback handling
                                        chrome.runtime.sendMessage = function(extensionIdOrMessage, messageOrCallback, optionsOrCallback, callback) {
                                            console.log("chrome.runtime.sendMessage called:", extensionIdOrMessage, messageOrCallback);
                                            
                                            // Handle different parameter combinations
                                            let extensionId, message, options;
                                            
                                            if (typeof extensionIdOrMessage === 'string') {
                                                extensionId = extensionIdOrMessage;
                                                message = messageOrCallback;
                                                
                                                if (typeof optionsOrCallback === 'function') {
                                                    callback = optionsOrCallback;
                                                    options = {};
                                                } else {
                                                    options = optionsOrCallback || {};
                                                    // callback remains as is
                                                }
                                            } else {
                                                extensionId = chrome.runtime.id;
                                                message = extensionIdOrMessage;
                                                
                                                if (typeof messageOrCallback === 'function') {
                                                    callback = messageOrCallback;
                                                    options = {};
                                                } else {
                                                    options = messageOrCallback || {};
                                                    callback = optionsOrCallback;
                                                }
                                            }
                                            
                                            // Generate a unique message ID
                                            const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
                                            
                                            // Store the callback for later use
                                            if (typeof callback === 'function') {
                                                if (!window.channel) {
                                                    window.channel = { execCallbacks: {} };
                                                }
                                                if (!window.channel.execCallbacks) {
                                                    window.channel.execCallbacks = {};
                                                }
                                                
                                                window.channel.execCallbacks[messageId] = callback;
                                                console.log("Stored callback for message ID:", messageId);
                                            }
                                            
                                            // Prepare the message to be sent to Python
                                            const messageData = {
                                                id: messageId,
                                                extensionId: extensionId,
                                                message: message,
                                                options: options,
                                                source: window.location.href,
                                                type: 'runtime.sendMessage'
                                            };
                                            
                                            // Send the message through the transport
                                            if (qt && qt.webChannelTransport) {
                                                qt.webChannelTransport.send(JSON.stringify(messageData));
                                                console.log("Message sent through transport:", messageData);
                                            } else {
                                                console.error("qt.webChannelTransport not available for sending message");
                                            }
                                            
                                            // Notify any registered message listeners in this context
                                            setTimeout(() => {
                                                const messageListeners = chrome.runtime.onMessage.listeners;
                                                for (let i = 0; i < messageListeners.length; i++) {
                                                    try {
                                                        const listener = messageListeners[i];
                                                        const sender = { id: extensionId };
                                                        const sendResponse = function(response) {
                                                            console.log("sendResponse called with:", response);
                                                            if (typeof callback === 'function') {
                                                                callback(response);
                                                            }
                                                        };
                                                        
                                                        listener(message, sender, sendResponse);
                                                    } catch (e) {
                                                        console.error("Error in message listener:", e);
                                                    }
                                                }
                                            }, 0);
                                            
                                            return true;
                                        };
                                        
                                        // Implement connect with proper port objects
                                        chrome.runtime.connect = function(extensionIdOrOptions, options) {
                                            console.log("chrome.runtime.connect called:", extensionIdOrOptions, options);
                                            
                                            // Handle different parameter combinations
                                            let extensionId, connectInfo;
                                            
                                            if (typeof extensionIdOrOptions === 'string') {
                                                extensionId = extensionIdOrOptions;
                                                connectInfo = options || {};
                                            } else {
                                                extensionId = chrome.runtime.id;
                                                connectInfo = extensionIdOrOptions || {};
                                            }
                                            
                                            // Generate a unique port ID
                                            const portId = 'port_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
                                            
                                            // Create a port object
                                            const port = {
                                                name: connectInfo.name || '',
                                                sender: { id: extensionId },
                                                disconnect: function() {
                                                    console.log("Port disconnected:", portId);
                                                    // Notify Python about the disconnection
                                                    if (qt && qt.webChannelTransport) {
                                                        qt.webChannelTransport.send(JSON.stringify({
                                                            type: 'runtime.disconnect',
                                                            portId: portId
                                                        }));
                                                    }
                                                    
                                                    // Notify any disconnect listeners
                                                    const disconnectListeners = port.onDisconnect.listeners;
                                                    for (let i = 0; i < disconnectListeners.length; i++) {
                                                        try {
                                                            disconnectListeners[i](port);
                                                        } catch (e) {
                                                            console.error("Error in port disconnect listener:", e);
                                                        }
                                                    }
                                                },
                                                postMessage: function(message) {
                                                    console.log("Port.postMessage called:", { portId, message });
                                                    // Send the message through the transport
                                                    if (qt && qt.webChannelTransport) {
                                                        qt.webChannelTransport.send(JSON.stringify({
                                                            type: 'runtime.portMessage',
                                                            portId: portId,
                                                            message: message
                                                        }));
                                                    }
                                                    
                                                    // Simulate a response for testing
                                                    setTimeout(() => {
                                                        const messageListeners = port.onMessage.listeners;
                                                        for (let i = 0; i < messageListeners.length; i++) {
                                                            try {
                                                                messageListeners[i]({
                                                                    received: true,
                                                                    echo: message,
                                                                    from: 'simulated',
                                                                    timestamp: Date.now()
                                                                });
                                                            } catch (e) {
                                                                console.error("Error in port message listener:", e);
                                                            }
                                                        }
                                                    }, 50);
                                                },
                                                onMessage: createEventTarget(),
                                                onDisconnect: createEventTarget()
                                            };
                                            
                                            // Store the port for later use
                                            if (!window.__ports) {
                                                window.__ports = {};
                                            }
                                            window.__ports[portId] = port;
                                            
                                            // Notify Python about the connection
                                            if (qt && qt.webChannelTransport) {
                                                qt.webChannelTransport.send(JSON.stringify({
                                                    type: 'runtime.connect',
                                                    portId: portId,
                                                    extensionId: extensionId,
                                                    connectInfo: connectInfo
                                                }));
                                            }
                                            
                                            // Notify listeners about the connection
                                            const connectListeners = window.chrome.runtime.onConnect.listeners;
                                            for (let i = 0; i < connectListeners.length; i++) {
                                                try {
                                                    connectListeners[i](port);
                                                } catch (e) {
                                                    console.error("Error in onConnect listener:", e);
                                                }
                                            }
                                            
                                            return port;
                                        };
                                        
                                        // Function to handle incoming messages from Python
                                        window.__handleRuntimeMessage = function(message, sender) {
                                            console.log("Handling runtime message:", message, sender);
                                            
                                            // Ensure chrome.runtime.onMessage exists and has listeners
                                            if (!chrome.runtime || !chrome.runtime.onMessage || !chrome.runtime.onMessage.listeners) {
                                                console.error("chrome.runtime.onMessage not properly initialized");
                                                return;
                                            }
                                            
                                            const messageListeners = chrome.runtime.onMessage.listeners;
                                            console.log("Number of message listeners:", messageListeners.length);
                                            
                                            let responseSent = false;
                                            
                                            for (let i = 0; i < messageListeners.length; i++) {
                                                try {
                                                    const listener = messageListeners[i];
                                                    if (typeof listener !== 'function') {
                                                        console.error("Listener is not a function:", listener);
                                                        continue;
                                                    }
                                                    
                                                    const sendResponse = function(response) {
                                                        if (responseSent) {
                                                            console.warn("sendResponse called more than once");
                                                            return;
                                                        }
                                                        responseSent = true;
                                                        console.log("Sending response:", response);
                                                        
                                                        // Send the response back through the transport
                                                        if (qt && qt.webChannelTransport) {
                                                            qt.webChannelTransport.send(JSON.stringify({
                                                                type: 'runtime.sendResponse',
                                                                messageId: message.id,
                                                                response: response
                                                            }));
                                                        }
                                                    };
                                                    
                                                    const keepChannelOpen = listener(message.data, sender, sendResponse);
                                                    if (!keepChannelOpen && !responseSent) {
                                                        // If the listener didn't return true and didn't call sendResponse,
                                                        // we should close the message channel
                                                        responseSent = true;
                                                    }
                                                } catch (e) {
                                                    console.error("Error in message listener:", e);
                                                }
                                            }
                                        };
                                        
                                        // Function to handle incoming port messages from Python
                                        window.__handlePortMessage = function(portId, message) {
                                            console.log("Handling port message:", portId, message);
                                            
                                            if (window.__ports && window.__ports[portId]) {
                                                const port = window.__ports[portId];
                                                const messageListeners = port.onMessage.listeners;
                                                
                                                for (let i = 0; i < messageListeners.length; i++) {
                                                    try {
                                                        messageListeners[i](message);
                                                    } catch (e) {
                                                        console.error("Error in port message listener:", e);
                                                    }
                                                }
                                            } else {
                                                console.error("Port not found:", portId);
                                            }
                                        };
                                        
                                        console.log("chrome.runtime API initialized with event listeners");
                                    }, 10);
                                }
                            } else if (jsonMessage.type === "invoke") {
                                // Simulate a response to method invocation
                                if (this.onmessage) {
                                    var response = {
                                        "id": jsonMessage.id,
                                        "data": {}
                                    };
                                    
                                    // Handle specific method calls
                                    if (jsonMessage.method === "extensionLoaded") {
                                        console.log("Extension loaded method called with args:", jsonMessage.args);
                                        // No specific response needed
                                    } else if (jsonMessage.method === "getManifest") {
                                        response.data = JSON.stringify({
                                            "name": "Test Extension",
                                            "version": "1.0",
                                            "manifest_version": 3,
                                            "permissions": ["storage"]
                                        });
                                    } else if (jsonMessage.method === "sendMessage") {
                                        console.log("sendMessage called with args:", jsonMessage.args);
                                        // Simulate async response via callback
                                        setTimeout(() => {
                                            var callbackResponse = {
                                                "id": jsonMessage.id + "_callback",
                                                "data": JSON.stringify({
                                                    "success": true,
                                                    "from": "background",
                                                    "received": true,
                                                    "timestamp": Date.now()
                                                })
                                            };
                                            this.onmessage(JSON.stringify(callbackResponse));
                                            
                                            // Also notify any registered message listeners
                                            if (chrome && chrome.runtime && chrome.runtime.onMessage) {
                                                const message = JSON.parse(jsonMessage.args[1]);
                                                chrome.runtime.onMessage.listeners.forEach(listener => {
                                                    try {
                                                        listener(message, { id: chrome.runtime.id }, function(response) {
                                                            console.log("Message listener response:", response);
                                                        });
                                                    } catch (e) {
                                                        console.error("Error in message listener:", e);
                                                    }
                                                });
                                            }
                                        }, 50);
                                    }
                                    
                                    this.onmessage(JSON.stringify(response));
                                }
                            }
                        } catch (e) {
                            console.error("Error processing message:", e);
                        }
                    },
                    onmessage: null
                };
            }
            
            // Set the global flag that the runtime API checks
            window.__qtWebChannelTransportReady = true;
            
            // Dispatch events to notify listeners that the transport is ready
            document.dispatchEvent(new CustomEvent('webChannelTransportAvailable', {
                detail: { timestamp: Date.now() }
            }));
            
            // Create a mock channel object with the necessary structure
            const mockChannel = {
                objects: {
                    runtimeBridge: {
                        __id__: "runtimeBridge",
                        messageReceived: {
                            connect: function(callback) {
                                console.log("Connected to messageReceived signal");
                                // Store the callback for later use
                                if (!window.__messageReceivedCallbacks) {
                                    window.__messageReceivedCallbacks = [];
                                }
                                window.__messageReceivedCallbacks.push(callback);
                            }
                        },
                        portMessageReceived: {
                            connect: function(callback) {
                                console.log("Connected to portMessageReceived signal");
                                // Store the callback for later use
                                if (!window.__portMessageReceivedCallbacks) {
                                    window.__portMessageReceivedCallbacks = [];
                                }
                                window.__portMessageReceivedCallbacks.push(callback);
                            }
                        }
                    }
                },
                execCallbacks: {}
            };
            
            // Store the mock channel globally
            window.channel = mockChannel;
            
            document.dispatchEvent(new CustomEvent('webChannelReady', {
                detail: { channel: mockChannel }
            }));
            
            console.log("Transport object made global and events dispatched");
            return true;
        })()
        """
        
        # Run the script to make the transport object global
        page.runJavaScript(make_transport_global_script, lambda result:
            print(f"Made transport object global: {result}"))
        
        # Then run the main initialization script
        script = """
// Global tracking variables
window.__qtWebChannelTransportReady = false;
window.__qwebchannel_init_attempts = 0;
const MAX_RETRIES = 50;

function initializeWebChannel() {
    if (typeof qt !== 'undefined' && qt.webChannelTransport) {
        try {
            // Check if QWebChannel is defined
            if (typeof QWebChannel === 'undefined') {
                console.log("QWebChannel is not defined yet, will retry later");
                return false;
            }
            
            console.log("Creating new QWebChannel with transport:", qt.webChannelTransport);
            new QWebChannel(qt.webChannelTransport, function(channel) {
                window.channel = channel;
                
                if (channel.objects && channel.objects.runtimeBridge) {
                    window.runtimeBridge = channel.objects.runtimeBridge;
                    window.__qtWebChannelTransportReady = true;
                    console.log("QWebChannel initialized successfully with RuntimeBridge");
                    
                    // Make chrome.runtime available globally
                    if (window.runtimeBridge) {
                        console.log("RuntimeBridge is available, initializing chrome.runtime API");
                        // Expose chrome.runtime API
                        if (typeof chrome === 'undefined') {
                            window.chrome = {};
                        }
                        if (typeof chrome.runtime === 'undefined') {
                            chrome.runtime = {};
                        }
                        
                        // Define basic chrome.runtime API methods
                        chrome.runtime.id = window.location.hostname;
                        chrome.runtime.sendMessage = function(extensionId, message, options, callback) {
                            if (typeof extensionId !== 'string') {
                                callback = options;
                                options = message;
                                message = extensionId;
                                extensionId = window.location.hostname;
                            }
                            // Always pass an options parameter, even if it's empty
                            if (typeof options === 'undefined') {
                                options = {};
                            }
                            return window.runtimeBridge.sendMessage(extensionId, JSON.stringify(message), JSON.stringify(options));
                        };
                    }
                } else {
                    console.error("Channel created but runtimeBridge is not available:", channel.objects);
                }
                
                // Dispatch an event to notify that the channel is ready
                document.dispatchEvent(new CustomEvent('webChannelReady', {
                    detail: { channel: channel }
                }));
                
                // Also dispatch the chrome.runtime.initialized event
                document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {
                    detail: { extensionId: window.location.hostname }
                }));
            });
            return true;
        } catch (e) {
            console.error("Error initializing QWebChannel:", e);
            return false;
        }
    } else {
        console.log("qt.webChannelTransport not available yet, will retry later");
        return false;
    }
}

function checkTransport() {
    if (typeof qt !== 'undefined' && qt.webChannelTransport) {
        console.log('qt.webChannelTransport is available');
        return true;
    }
    return false;
}

// Check if QWebChannel is defined
function isQWebChannelDefined() {
    return typeof QWebChannel !== 'undefined';
}

// Try to initialize immediately
if (!isQWebChannelDefined()) {
    console.log("QWebChannel is not defined yet, waiting for it to load...");
    
    // Set up a retry mechanism specifically for QWebChannel loading
    let qwebchannelCheckCount = 0;
    const MAX_QWEBCHANNEL_CHECKS = 50;
    
    const qwebchannelCheckInterval = setInterval(function() {
        qwebchannelCheckCount++;
        console.log("Checking if QWebChannel is defined... (attempt " + qwebchannelCheckCount + " of " + MAX_QWEBCHANNEL_CHECKS + ")");
        
        if (isQWebChannelDefined()) {
            console.log("QWebChannel is now defined, attempting initialization");
            clearInterval(qwebchannelCheckInterval);
            
            // Now try to initialize with the transport
            if (!initializeWebChannel()) {
                // If initialization fails, set up the transport retry mechanism
                let transportRetryCount = 0;
                const MAX_TRANSPORT_RETRIES = 50;
                
                // Set up a MutationObserver to detect when qt object might be injected
                var observer = new MutationObserver(function(mutations) {
                    if (checkTransport() && !window.__qtWebChannelTransportReady) {
                        console.log("MutationObserver detected qt.webChannelTransport");
                        initializeWebChannel();
                        if (window.__qtWebChannelTransportReady) {
                            observer.disconnect();
                        }
                    }
                });
                
                // Start observing
                observer.observe(document, {
                    childList: true,
                    subtree: true,
                    attributes: true
                });
                
                // Also use interval-based retry
                const retryInterval = setInterval(function() {
                    transportRetryCount++;
                    console.log("Retrying QWebChannel initialization... (attempt " + transportRetryCount + " of " + MAX_TRANSPORT_RETRIES + ")");
                    
                    // Ensure qt and transport objects exist
                    if (typeof qt === 'undefined') {
                        console.log("Creating qt object during retry");
                        window.qt = {};
                    }
                    
                    if (!qt.webChannelTransport) {
                        console.log("Creating qt.webChannelTransport object during retry");
                        qt.webChannelTransport = {};
                    }
                    
                    if (initializeWebChannel() || transportRetryCount >= MAX_TRANSPORT_RETRIES) {
                        clearInterval(retryInterval);
                        observer.disconnect();
                        if (transportRetryCount >= MAX_TRANSPORT_RETRIES) {
                            console.error("Failed to initialize QWebChannel after " + MAX_TRANSPORT_RETRIES + " attempts");
                        }
                    }
                }, 200);
            }
        } else if (qwebchannelCheckCount >= MAX_QWEBCHANNEL_CHECKS) {
            console.error("QWebChannel was never defined after " + MAX_QWEBCHANNEL_CHECKS + " checks");
            clearInterval(qwebchannelCheckInterval);
        }
    }, 100); // Check more frequently for QWebChannel definition
} else if (!initializeWebChannel()) {
    // QWebChannel is defined but initialization failed, set up the transport retry mechanism
    let retryCount = 0;
    
    // Set up a MutationObserver to detect when qt object might be injected
    var observer = new MutationObserver(function(mutations) {
        if (checkTransport() && !window.__qtWebChannelTransportReady) {
            console.log("MutationObserver detected qt.webChannelTransport");
            initializeWebChannel();
            if (window.__qtWebChannelTransportReady) {
                observer.disconnect();
            }
        }
    });
    
    // Start observing
    observer.observe(document, {
        childList: true,
        subtree: true,
        attributes: true
    });
    
    // Also use interval-based retry
    const retryInterval = setInterval(function() {
        retryCount++;
        console.log("Retrying QWebChannel initialization... (attempt " + retryCount + " of " + MAX_RETRIES + ")");
        
        // Ensure qt and transport objects exist
        if (typeof qt === 'undefined') {
            console.log("Creating qt object during retry");
            window.qt = {};
        }
        
        if (!qt.webChannelTransport) {
            console.log("Creating qt.webChannelTransport object during retry");
            qt.webChannelTransport = {};
        }
        
        if (initializeWebChannel() || retryCount >= MAX_RETRIES) {
            clearInterval(retryInterval);
            observer.disconnect();
            if (retryCount >= MAX_RETRIES) {
                console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
            }
        }
    }, 200);
    
    // Stop checking after 20 seconds to prevent memory leaks
    setTimeout(function() {
        observer.disconnect();
        clearInterval(retryInterval);
        if (!window.__qtWebChannelTransportReady) {
            console.error('Failed to initialize QWebChannel after 20 seconds');
        }
    }, 20000);
}

// Add an event listener for manual transport requests
document.addEventListener('requestWebChannelTransport', function() {
    console.log("Received request for webChannelTransport");
    if (typeof qt !== 'undefined' && qt.webChannelTransport) {
        console.log("qt.webChannelTransport is available, initializing QWebChannel");
        initializeWebChannel();
    } else {
        console.log("qt.webChannelTransport still not available after request");
        
        // Try to manually create the transport object as a last resort
        if (typeof qt === 'undefined') {
            console.log("Attempting to create qt object");
            window.qt = {};
        }
        
        if (!qt.webChannelTransport) {
            console.log("Creating qt.webChannelTransport object");
            qt.webChannelTransport = {};
        }
        
        // Notify that we need the transport object
        document.dispatchEvent(new CustomEvent('webChannelTransportRequested', {
            detail: { timestamp: Date.now() }
        }));
        
        // Try initialization again
        initializeWebChannel();
    }
});

// Helper function to check if chrome.runtime is initialized
window.isChromeRuntimeInitialized = function() {
    return window.__qtWebChannelTransportReady === true;
};
"""

    def on_load_finished(self, success):
        """Handle the page load finishing."""
        self.is_loading = False
        self.loadStatusChanged.emit()
        if success:
            current_url = self.web_view.url().toString()
            print(f"Page loaded successfully: {current_url}")
            
            # Update the title if available
            title = self.web_view.title()
            if title:
                self.titleChanged.emit(title)
            
            # Check if we need to inject content scripts
            if hasattr(self.parent, 'window') and isinstance(self.parent.window(), Browser):
                browser = self.parent.window()
                if hasattr(browser, 'content_script_injector'):
                    # Only inject content scripts for http/https URLs
                    if current_url.startswith(('http://', 'https://')):
                        self._inject_content_scripts(current_url, browser.content_script_injector)
        else:
            print(f"Failed to load page: {self.url}")
    
    def _inject_content_scripts(self, url, injector):
        """Inject matching content scripts into the page."""
        matching_scripts = injector.get_scripts_for_url(url)
        if not matching_scripts:
            return
            
        print(f"Found {len(matching_scripts)} matching content scripts for {url}")
        
        for script_def in matching_scripts:
            # Create the injection script
            injection_script = injector.create_injection_script(script_def)
            
            # Inject the script into the page
            self.web_view.page().runJavaScript(
                injection_script,
                lambda result: print(f"Content script injection completed for {script_def['extension_id']}")
            )
            
            print(f"Injecting content scripts from {script_def['extension_id']}: {script_def['js']}")
    
    # We no longer need process handling methods since we're using QWebEngineView directly
    
    def close(self):
        """Close the tab."""
        # Close the web view
        if hasattr(self, 'web_view'):
            self.web_view.close()
        
        super().close()


class ContentScriptInjector:
    """
    Handles the injection of extension content scripts into web pages.
    Parses extension manifests to find content scripts and injects them
    into matching web pages.
    """
    def __init__(self, extensions_dir, assets_extensions_dir="assets/browser/extensions"):
        self.extensions_dir = extensions_dir
        self.assets_extensions_dir = assets_extensions_dir
        self.content_scripts = []  # List of content script definitions
        self.load_content_scripts()
    
    def load_content_scripts(self):
        """Load content script definitions from all installed extensions."""
        print("Loading content scripts from extensions...")
        self.content_scripts = []
        
        # First check the profile extensions directory
        if os.path.exists(self.extensions_dir):
            for ext_name in os.listdir(self.extensions_dir):
                if ext_name.endswith(".disabled"):
                    continue
                    
                ext_path = os.path.join(self.extensions_dir, ext_name)
                if os.path.isdir(ext_path):
                    self._load_extension_content_scripts(ext_path, ext_name)
        
        # Then check the assets extensions directory
        if os.path.exists(self.assets_extensions_dir):
            for ext_name in os.listdir(self.assets_extensions_dir):
                if ext_name.endswith(".disabled"):
                    continue
                    
                ext_path = os.path.join(self.assets_extensions_dir, ext_name)
                if os.path.isdir(ext_path):
                    # Only load if not already loaded from profile
                    if not os.path.exists(os.path.join(self.extensions_dir, ext_name)):
                        self._load_extension_content_scripts(ext_path, ext_name)
        
        print(f"Loaded {len(self.content_scripts)} content script definitions")
    
    def _load_extension_content_scripts(self, ext_path, ext_name):
        """Load content scripts from a specific extension."""
        manifest_path = os.path.join(ext_path, "manifest.json")
        if not os.path.exists(manifest_path):
            return
            
        try:
            with open(manifest_path, 'r', encoding='utf-8') as f:
                manifest = json.load(f)
                
            if 'content_scripts' not in manifest:
                return
                
            for script_def in manifest['content_scripts']:
                if 'matches' not in script_def or 'js' not in script_def:
                    continue
                    
                # Add the extension path and ID to the definition
                script_def['extension_path'] = ext_path
                script_def['extension_id'] = ext_name
                self.content_scripts.append(script_def)
                
                print(f"Loaded content script from {ext_name}: {script_def['js']}")
        except Exception as e:
            print(f"Error loading content scripts from {ext_name}: {e}")
    
    def get_scripts_for_url(self, url):
        """Get all content scripts that match the given URL."""
        matching_scripts = []
        
        for script_def in self.content_scripts:
            if self._url_matches_patterns(url, script_def['matches']):
                matching_scripts.append(script_def)
                
        return matching_scripts
    
    def _url_matches_patterns(self, url, patterns):
        """Check if a URL matches any of the given patterns."""
        for pattern in patterns:
            # Handle special pattern <all_urls>
            if pattern == "<all_urls>":
                return True
                
            # Convert the match pattern to a regex
            regex_pattern = self._convert_match_pattern_to_regex(pattern)
            if regex_pattern and re.match(regex_pattern, url):
                return True
                
        return False
    
    def _convert_match_pattern_to_regex(self, pattern):
        """Convert a Chrome extension match pattern to a regex."""
        # Handle special cases
        if pattern == "<all_urls>":
            return r"^(http|https|file|ftp)://.*"
            
        # Basic pattern validation
        if not pattern or not ("://" in pattern):
            return None
            
        # Parse the pattern
        parts = pattern.split("://")
        if len(parts) != 2:
            return None
            
        scheme, rest = parts
        
        # Handle scheme
        if scheme == "*":
            scheme_regex = "(http|https)"
        elif scheme in ["http", "https", "file", "ftp"]:
            scheme_regex = scheme
        else:
            return None
            
        # Split host and path
        if "/" in rest:
            host, path = rest.split("/", 1)
            path = "/" + path
        else:
            host = rest
            path = "/"
            
        # Handle host
        if host == "*":
            host_regex = ".*"
        elif host.startswith("*."):
            host_regex = "(.*\\.)?" + re.escape(host[2:])
        else:
            host_regex = re.escape(host)
            
        # Handle path
        path_regex = ""
        if path == "/*":
            path_regex = "/.*"
        elif path.endswith("/*"):
            path_regex = re.escape(path[:-1]) + ".*"
        else:
            path_regex = re.escape(path)
            
        # Combine into final regex
        return f"^{scheme_regex}://{host_regex}{path_regex}$"
    
    def create_injection_script(self, script_def, web_channel_init=True):
        """Create a script to inject the content scripts into the page."""
        extension_id = script_def['extension_id']
        extension_path = script_def['extension_path']
        js_files = script_def['js']
        
        # Build the injection script
        injection_script = ""
        
        # Add WebChannel initialization if requested
        if web_channel_init:
            # First load QWebChannel.js content
            qwebchannel_path = os.path.join("assets/browser/js", "qwebchannel.js")
            qwebchannel_js = ""
            try:
                with open(qwebchannel_path, 'r', encoding='utf-8') as f:
                    qwebchannel_js = f.read()
            except Exception as e:
                print(f"Error loading QWebChannel.js for content script: {e}")
                qwebchannel_js = "console.error('Failed to load QWebChannel.js');"
            
            injection_script += f"""
            // Initialize WebChannel for content script
            (function() {{
                // Directly inject QWebChannel.js content
                {qwebchannel_js}
                
                console.log("QWebChannel.js directly injected into content script <ContentScriptInjector.create_injection_script>");
                
                // Verify QWebChannel is defined
                if (typeof QWebChannel === 'undefined') {{
                    console.error("QWebChannel is still undefined after direct injection in content script!");
                }} else {{
                    console.log("QWebChannel is successfully defined in content script global scope");
                }}
                
                window.__contentScriptExtensionId = "{extension_id}";
                
                // Create a global variable to track if transport is ready
                window.__qtWebChannelTransportReady = false;
                
                function initializeWebChannel() {{
                    if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                        try {{
                            // QWebChannel should be defined now
                            new QWebChannel(qt.webChannelTransport, function(channel) {{
                                window.channel = channel;
                                window.runtimeBridge = channel.objects.runtimeBridge;
                                window.__qtWebChannelTransportReady = true;
                                console.log("QWebChannel initialized successfully for content script");
                                
                                // Dispatch an event to notify that the channel is ready
                                document.dispatchEvent(new CustomEvent('webChannelReady', {{
                                    detail: {{ channel: channel }}
                                }}));
                                
                                // Also dispatch the chrome.runtime.initialized event
                                document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {{
                                    detail: {{ extensionId: "{extension_id}" }}
                                }}));
                                
                                // Make chrome.runtime available globally
                                if (window.runtimeBridge) {{
                                    console.log("RuntimeBridge is available, initializing chrome.runtime API for content script");
                                    // Expose chrome.runtime API
                                    if (typeof chrome === 'undefined') {{
                                        window.chrome = {{}};
                                    }}
                                    if (typeof chrome.runtime === 'undefined') {{
                                        chrome.runtime = {{}};
                                    }}
                                    
                                    // Define basic chrome.runtime API methods
                                    chrome.runtime.id = "{extension_id}";
                                    chrome.runtime.sendMessage = function(extensionId, message, options, callback) {{
                                        if (typeof extensionId !== 'string') {{
                                            callback = options;
                                            options = message;
                                            message = extensionId;
                                            extensionId = "{extension_id}";
                                        }}
                                        // Always pass an options parameter, even if it's empty
                                        if (typeof options === 'undefined') {{
                                            options = {{}};
                                        }}
                                        return window.runtimeBridge.sendMessage(extensionId, JSON.stringify(message), JSON.stringify(options));
                                    }};
                                }}
                            }});
                            return true;
                        }} catch (e) {{
                            console.error("Error initializing QWebChannel in content script:", e);
                            return false;
                        }}
                    }} else {{
                        console.log("qt.webChannelTransport not available yet in content script, will retry later");
                        return false;
                    }}
                }}
                
                // Create a global variable to track initialization attempts
                window.__qwebchannel_init_attempts = 0;
                
                // Try to initialize immediately
                if (!initializeWebChannel()) {{
                    // If it fails, set up a retry mechanism
                    const MAX_RETRIES = 50;
                    
                    const retryInterval = setInterval(function() {{
                        window.__qwebchannel_init_attempts++;
                        console.log("Retrying QWebChannel initialization in content script... (attempt " + window.__qwebchannel_init_attempts + " of " + MAX_RETRIES + ")");
                        
                        // Check if qt object exists, if not, try to create it
                        if (typeof qt === 'undefined') {{
                            console.log("qt object is undefined in content script, creating placeholder");
                            window.qt = {{}};
                        }}
                        
                        if (initializeWebChannel() || window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                            clearInterval(retryInterval);
                            if (window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                                console.error("Failed to initialize QWebChannel in content script after " + MAX_RETRIES + " attempts");
                            }}
                        }}
                    }}, 200);
                }}
            }})();
            """
        
        # Add code to load each content script
        for js_file in js_files:
            script_url = f"qextension://{extension_id}/{js_file}"
            injection_script += f"""
            (function() {{
                console.log("Injecting content script: {script_url}");
                
                // Create a script element to load the content script
                var script = document.createElement('script');
                script.src = "{script_url}";
                script.type = "text/javascript";
                script.async = false;
                
                // Add error handling
                script.onerror = function() {{
                    console.error("Failed to load content script: {script_url}");
                }};
                
                // Add the script to the page
                (document.head || document.documentElement).appendChild(script);
            }})();
            """
        
        return injection_script


class ChromeUrlInterceptor(QWebEngineUrlRequestInterceptor):
    """
    Intercepts URL requests to handle chrome:// URLs specially
    """
    def __init__(self, browser):
        super().__init__()
        self.browser = browser
    
    def interceptRequest(self, info):
        url = info.requestUrl()
        if url.scheme() == "chrome":
            print(f"Intercepted chrome:// URL: {url.toString()}")
            # We don't block the request, just log it for debugging
            # The actual handling is done by ChromeWebEnginePage


class ExtensionSchemeHandler(QWebEngineUrlSchemeHandler):
    """A custom URL scheme handler for loading extension files."""
    def __init__(self, extensions_dir, parent=None):
        super().__init__(parent)
        self.extensions_dir = extensions_dir
        self.jobs = {}  # Keep track of active jobs and their buffers
        self.assets_extensions_dir = "assets/browser/extensions"  # Path to the source extensions

    def requestStarted(self, job: QWebEngineUrlRequestJob):
        url = job.requestUrl()
        ext_id = url.host()
        resource_path = url.path().lstrip('/')

        # First try to load from the profile's extensions directory
        file_path = os.path.abspath(os.path.join(self.extensions_dir, ext_id, resource_path))
        
        # If not found, try the assets extensions directory
        if not os.path.exists(file_path):
            assets_file_path = os.path.abspath(os.path.join(self.assets_extensions_dir, ext_id, resource_path))
            if os.path.exists(assets_file_path):
                file_path = assets_file_path
        
        try:
            with open(file_path, 'rb') as f:
                content = f.read()

            mime_type, _ = mimetypes.guess_type(file_path)
            if mime_type:
                mime_type = mime_type.encode()
            else:
                # Special handling for JavaScript files
                if file_path.endswith('.js'):
                    mime_type = b'application/javascript'
                else:
                    mime_type = b'application/octet-stream'

            # Special handling for HTML files - inject WebChannel initialization code
            if file_path.endswith(('.html', '.htm')) and mime_type in (b'text/html', b'application/xhtml+xml'):
                print(f"Injecting WebChannel initialization into HTML file: {file_path}")
                try:
                    # Decode the HTML content
                    html_content = content.decode('utf-8')
                    
                    # Load QWebChannel.js
                    qwebchannel_path = os.path.join("assets/browser/js", "qwebchannel.js")
                    with open(qwebchannel_path, 'r', encoding='utf-8') as f:
                        qwebchannel_js = f.read()
                    
                    # Create the WebChannel initialization script with direct QWebChannel.js injection
                    init_script = f"""
                    <script>
                    // Directly define QWebChannel in the global scope
                    {qwebchannel_js}
                    
                    console.log("QWebChannel.js directly injected into extension page");
                    
                    // Verify QWebChannel is defined
                    if (typeof QWebChannel === 'undefined') {{
                        console.error("QWebChannel is still undefined after direct injection!");
                    }} else {{
                        console.log("QWebChannel is successfully defined in global scope");
                    }}
                    
                    // WebChannel initialization
                    window.__qtWebChannelTransportReady = false;
                    
                    function initializeWebChannel() {{
                        if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                            try {{
                                // QWebChannel should be defined now
                                new QWebChannel(qt.webChannelTransport, function(channel) {{
                                    window.channel = channel;
                                    window.runtimeBridge = channel.objects.runtimeBridge;
                                    window.__qtWebChannelTransportReady = true;
                                    console.log("QWebChannel initialized successfully for extension: {ext_id}");
                                    
                                    // Dispatch an event to notify that the channel is ready
                                    document.dispatchEvent(new CustomEvent('webChannelReady', {{
                                        detail: {{ channel: channel }}
                                    }}));
                                    
                                    // Also dispatch the chrome.runtime.initialized event
                                    document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {{
                                        detail: {{ extensionId: "{ext_id}" }}
                                    }}));
                                    
                                    // Make chrome.runtime available globally
                                    if (window.runtimeBridge) {{
                                        console.log("RuntimeBridge is available, initializing chrome.runtime API");
                                        // Expose chrome.runtime API
                                        if (typeof chrome === 'undefined') {{
                                            window.chrome = {{}};
                                        }}
                                        if (typeof chrome.runtime === 'undefined') {{
                                            chrome.runtime = {{}};
                                        }}
                                        
                                        // Define basic chrome.runtime API methods
                                        chrome.runtime.id = "{ext_id}";
                                        chrome.runtime.sendMessage = function(extensionId, message, options, callback) {{
                                            if (typeof extensionId !== 'string') {{
                                                callback = options;
                                                options = message;
                                                message = extensionId;
                                                extensionId = "{ext_id}";
                                            }}
                                            // Always pass an options parameter, even if it's empty
                                            if (typeof options === 'undefined') {{
                                                options = {{}};
                                            }}
                                            return window.runtimeBridge.sendMessage(extensionId, JSON.stringify(message), JSON.stringify(options));
                                        }};
                                    }}
                                }});
                                return true;
                            }} catch (e) {{
                                console.error("Error initializing QWebChannel:", e);
                                return false;
                            }}
                        }} else {{
                            console.log("qt.webChannelTransport not available yet, will retry later");
                            return false;
                        }}
                    }}
                    
                    // Create a global variable to track initialization attempts
                    window.__qwebchannel_init_attempts = 0;
                    
                    // Try to initialize immediately
                    if (!initializeWebChannel()) {{
                        // If it fails, set up a retry mechanism
                        const MAX_RETRIES = 50;
                        
                        const retryInterval = setInterval(function() {{
                            window.__qwebchannel_init_attempts++;
                            console.log("Retrying QWebChannel initialization... (attempt " + window.__qwebchannel_init_attempts + " of " + MAX_RETRIES + ")");
                            
                            // Check if qt object exists, if not, try to create it
                            if (typeof qt === 'undefined') {{
                                console.log("qt object is undefined, creating placeholder");
                                window.qt = {{}};
                            }}
                            
                            if (initializeWebChannel() || window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                                clearInterval(retryInterval);
                                if (window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                                    console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
                                }}
                            }}
                        }}, 200);
                        
                        // Add an event listener for manual transport requests
                        document.addEventListener('requestWebChannelTransport', function() {{
                            console.log("Received request for webChannelTransport");
                            if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                                console.log("qt.webChannelTransport is available, initializing QWebChannel");
                                initializeWebChannel();
                            }}
                        }});
                    }}
                    </script>
                    """
                    
                    # Inject the script right before the closing </head> tag
                    if '</head>' in html_content:
                        html_content = html_content.replace('</head>', init_script + '</head>')
                    else:
                        # If no </head> tag, inject at the beginning of the file
                        html_content = init_script + html_content
                    
                    # Convert back to bytes
                    content = html_content.encode('utf-8')
                    
                except Exception as e:
                    print(f"Error injecting WebChannel initialization: {e}")
                    # Continue with the original content if there's an error
            
            # Special handling for service worker scripts
            if resource_path.endswith('background.js'):
                print(f"Loading background script: {file_path}")
                # You might want to add special headers or processing for service workers

            buf = QBuffer(parent=self)
            buf.setData(content)
            buf.open(QBuffer.OpenModeFlag.ReadOnly)

            # Store buffer to prevent garbage collection
            self.jobs[job] = buf

            # Reply with the content
            job.reply(mime_type, buf)
            print(f"Replied with content for: {file_path}")

        except FileNotFoundError:
            print(f"Extension resource not found: {file_path}")
            job.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
            if job in self.jobs:
                del self.jobs[job]
        except Exception as e:
            print(f"Error loading extension resource {file_path}: {e}")
            job.fail(QWebEngineUrlRequestJob.Error.RequestFailed)
            if job in self.jobs:
                del self.jobs[job]


class ExtensionDialog(QDialog):
    """
    Dialog for managing browser extensions.
    """
    def __init__(self, parent=None, extensions_dir=None):
        super().__init__(parent)
        self.setWindowTitle("Browser Extensions")
        self.setMinimumSize(500, 400)
        
        self.extensions_dir = extensions_dir
        self.open_popups = []
        
        # Create layout
        layout = QVBoxLayout(self)
        
        # Create extensions list
        self.extensions_list = QListWidget()
        layout.addWidget(self.extensions_list)
        
        # Create buttons
        button_layout = QHBoxLayout()
        self.install_button = QPushButton("Install New")
        self.remove_button = QPushButton("Remove")
        self.enable_button = QPushButton("Enable/Disable")
        self.popup_button = QPushButton("Open Popup")
        self.close_button = QPushButton("Close")
        
        button_layout.addWidget(self.install_button)
        button_layout.addWidget(self.remove_button)
        button_layout.addWidget(self.enable_button)
        button_layout.addWidget(self.popup_button)
        button_layout.addStretch()
        button_layout.addWidget(self.close_button)
        
        layout.addLayout(button_layout)
        
        # Connect signals
        self.close_button.clicked.connect(self.accept)
        self.install_button.clicked.connect(self.install_extension)
        self.remove_button.clicked.connect(self.remove_extension)
        self.enable_button.clicked.connect(self.toggle_extension)
        self.popup_button.clicked.connect(self.open_extension_popup)
        
        # Load extensions
        self.load_extensions()
    
    def load_extensions(self):
        """Load and display installed extensions."""
        self.extensions_list.clear()
        
        if not self.extensions_dir or not os.path.exists(self.extensions_dir):
            self.extensions_list.addItem("No extensions directory found")
            return
        
        # Find all extension directories
        for ext_name in os.listdir(self.extensions_dir):
            ext_path = os.path.join(self.extensions_dir, ext_name)
            if os.path.isdir(ext_path):
                enabled = not ext_name.endswith(".disabled")
                base_name = ext_name[:-9] if not enabled else ext_name
                
                manifest_path = os.path.join(ext_path, "manifest.json")
                if os.path.exists(manifest_path):
                    try:
                        with open(manifest_path, 'r', encoding='utf-8') as f:
                            manifest = json.load(f)
                        
                        name = manifest.get("name", base_name)
                        version = manifest.get("version", "unknown")
                        status = "ACTIVE" if enabled else "INACTIVE"
                        
                        item = QListWidgetItem(f"{name} (v{version}) - {status}")
                        item.setData(Qt.ItemDataRole.UserRole, ext_path)
                        item.setCheckState(Qt.CheckState.Checked if enabled else Qt.CheckState.Unchecked)
                        
                        self.extensions_list.addItem(item)
                    except Exception as e:
                        print(f"Error loading extension manifest for {ext_name}: {e}")
                        item = QListWidgetItem(f"{ext_name} (Error loading manifest)")
                        item.setData(Qt.ItemDataRole.UserRole, ext_path)
                        self.extensions_list.addItem(item)
    
    def install_extension(self):
        """Install a new extension from a directory."""
        source_dir = QFileDialog.getExistingDirectory(self, "Select Extension Directory")
        if not source_dir:
            return
            
        ext_name = os.path.basename(source_dir)
        dest_dir = os.path.join(self.extensions_dir, ext_name)
        
        if os.path.exists(dest_dir):
            QMessageBox.warning(self, "Extension Exists", f"An extension named '{ext_name}' already exists.")
            return
            
        try:
            shutil.copytree(source_dir, dest_dir)
            self.load_extensions()
            QMessageBox.information(self, "Restart Required",
                                    "The extension has been installed. Please restart the browser for it to be loaded.")
        except Exception as e:
            QMessageBox.critical(self, "Installation Error", f"Could not install the extension: {e}")
    
    def remove_extension(self):
        """Remove the selected extension."""
        selected_items = self.extensions_list.selectedItems()
        if not selected_items:
            return
        
        for item in selected_items:
            ext_path = item.data(Qt.ItemDataRole.UserRole)
            if ext_path and os.path.exists(ext_path):
                try:
                    shutil.rmtree(ext_path)
                    print(f"Removed extension: {ext_path}")
                except Exception as e:
                    print(f"Error removing extension: {e}")
        
        self.load_extensions()
    
    def toggle_extension(self):
        """Toggle the enabled state of the selected extension and reload the profile."""
        selected_items = self.extensions_list.selectedItems()
        if not selected_items:
            return
        
        item = selected_items[0]
        ext_path = item.data(Qt.ItemDataRole.UserRole)
        ext_name = os.path.basename(ext_path)
        is_enabled = not ext_name.endswith(".disabled")
        
        if is_enabled:
            new_path = ext_path + ".disabled"
        else:
            new_path = ext_path[:-9]
            
        try:
            # Rename the folder to enable/disable the extension
            os.rename(ext_path, new_path)
            
            # Update the list in the dialog
            self.load_extensions()
            
            # Get the main browser window and trigger a profile reload
            browser = self.parent()
            if browser and hasattr(browser, 'reload_profile_and_tabs'):
                browser.reload_profile_and_tabs()
            else:
                # This is a fallback in case the parent isn't the browser window
                QMessageBox.information(self, "Restart Required",
                                        "Change applied. Please restart the browser for it to take effect.")

        except OSError as e:
            QMessageBox.critical(self, "Error", f"Could not change extension state: {e}")

    def open_extension_popup(self):
        """Open the popup of the selected extension, if it has one."""
        selected_items = self.extensions_list.selectedItems()
        if not selected_items:
            return
            
        item = selected_items[0]
        ext_path = item.data(Qt.ItemDataRole.UserRole)
        ext_name = os.path.basename(ext_path)

        if ext_name.endswith(".disabled"):
            QMessageBox.warning(self, "Extension Disabled", "Cannot open popup for a disabled extension.")
            return

        manifest_path = os.path.join(ext_path, "manifest.json")

        if not os.path.exists(manifest_path):
            QMessageBox.warning(self, "Error", "manifest.json not found for this extension.")
            return

        try:
            with open(manifest_path, 'r', encoding='utf-8') as f:
                manifest = json.load(f)
        except Exception as e:
            QMessageBox.critical(self, "Error", f"Could not read manifest.json: {e}")
            return

        popup_path = None
        action = manifest.get('action') or manifest.get('browser_action') or manifest.get('page_action')
        if action and action.get('default_popup'):
            popup_path = action.get('default_popup')

        if not popup_path:
            QMessageBox.information(self, "No Popup", "This extension does not have a popup.")
            return

        popup_file = os.path.join(ext_path, popup_path)
        if not os.path.exists(popup_file):
            QMessageBox.warning(self, "Error", f"Popup file not found: {popup_path}")
            return

        # Open the popup in a new dialog
        popup_dialog = QDialog(self)
        popup_dialog.setWindowTitle(manifest.get("name", "Extension Popup"))
        layout = QVBoxLayout(popup_dialog)
        web_view = QWebEngineView()
        
        browser_window = self.parent()
        if not browser_window or not isinstance(browser_window, Browser):
             QMessageBox.critical(self, "Error", "Could not get browser window reference.")
             return

        # Use the main browser's profile so the scheme handler is available
        page = QWebEnginePage(browser_window.profile, web_view)
        web_view.setPage(page)
        
        # Set up QWebChannel for the page to enable chrome.runtime API
        page.setWebChannel(browser_window.web_channel)
        
        # First load QWebChannel.js content
        qwebchannel_path = os.path.join("assets/browser/js", "qwebchannel.js")
        qwebchannel_js = ""
        try:
            with open(qwebchannel_path, 'r', encoding='utf-8') as f:
                qwebchannel_js = f.read()
        except Exception as e:
            print(f"Error loading QWebChannel.js: {e}")
            qwebchannel_js = "console.error('Failed to load QWebChannel.js');"
        
        # Explicitly initialize the QWebChannel with a script that includes QWebChannel.js
        init_script = f"""
            // Directly inject QWebChannel.js content
            {qwebchannel_js}
            
            console.log("QWebChannel.js directly injected into popup");
            
            // Verify QWebChannel is defined
            if (typeof QWebChannel === 'undefined') {{
                console.error("QWebChannel is still undefined after direct injection!");
            }} else {{
                console.log("QWebChannel is successfully defined in global scope");
            }}
            
            // Create a global variable to track if transport is ready
            window.__qtWebChannelTransportReady = false;
            
            function initializeWebChannel() {{
                if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                    try {{
                        // QWebChannel should be defined now
                        new QWebChannel(qt.webChannelTransport, function(channel) {{
                            window.channel = channel;
                            window.runtimeBridge = channel.objects.runtimeBridge;
                            window.__qtWebChannelTransportReady = true;
                            console.log("QWebChannel initialized successfully for extension popup");
                            
                            // Dispatch an event to notify that the channel is ready
                            document.dispatchEvent(new CustomEvent('webChannelReady', {{
                                detail: {{ channel: channel }}
                            }}));
                            
                            // Also dispatch the chrome.runtime.initialized event
                            document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {{
                                detail: {{ extensionId: "{ext_name}" }}
                            }}));
                            
                            // Make chrome.runtime available globally
                            if (window.runtimeBridge) {{
                                console.log("RuntimeBridge is available, initializing chrome.runtime API");
                                // Expose chrome.runtime API
                                if (typeof chrome === 'undefined') {{
                                    window.chrome = {{}};
                                }}
                                if (typeof chrome.runtime === 'undefined') {{
                                    chrome.runtime = {{}};
                                }}
                                
                                // Define basic chrome.runtime API methods
                                chrome.runtime.id = "{ext_name}";
                                chrome.runtime.sendMessage = function(extensionId, message, options, callback) {{
                                    if (typeof extensionId !== 'string') {{
                                        callback = options;
                                        options = message;
                                        message = extensionId;
                                        extensionId = "{ext_name}";
                                    }}
                                    // Always pass an options parameter, even if it's empty
                                    if (typeof options === 'undefined') {{
                                        options = {{}};
                                    }}
                                    return window.runtimeBridge.sendMessage(extensionId, JSON.stringify(message), JSON.stringify(options));
                                }};
                            }}
                        }});
                        return true;
                    }} catch (e) {{
                        console.error("Error initializing QWebChannel:", e);
                        return false;
                    }}
                }} else {{
                    console.log("qt.webChannelTransport not available yet, will retry later");
                    return false;
                }}
            }}
            
            // Create a global variable to track initialization attempts
            window.__qwebchannel_init_attempts = 0;
            
            // Try to initialize immediately
            if (!initializeWebChannel()) {{
                // If it fails, set up a retry mechanism
                const MAX_RETRIES = 50;
                
                const retryInterval = setInterval(function() {{
                    window.__qwebchannel_init_attempts++;
                    console.log("Retrying QWebChannel initialization... (attempt " + window.__qwebchannel_init_attempts + " of " + MAX_RETRIES + ")");
                    
                    // Check if qt object exists, if not, try to create it
                    if (typeof qt === 'undefined') {{
                        console.log("qt object is undefined, creating placeholder");
                        window.qt = {{}};
                    }}
                    
                    if (initializeWebChannel() || window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                        clearInterval(retryInterval);
                        if (window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                            console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
                        }}
                    }}
                }}, 200);
            }}
        """
        
        # Run the initialization script before loading the URL
        page.runJavaScript(init_script, lambda result: print("QWebChannel initialization script executed for popup"))

        popup_url = QUrl(f"qextension://{ext_name}/{popup_path}")
        web_view.load(popup_url)

        layout.addWidget(web_view)
        popup_dialog.setLayout(layout)
        popup_dialog.resize(400, 600)
        popup_dialog.show()
        self.open_popups.append(popup_dialog)
        popup_dialog.finished.connect(lambda: self.open_popups.remove(popup_dialog))


class DetachableTabBar(QTabBar):
    tabDetached = pyqtSignal(int, QPoint)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.drag_start_pos = QPoint()
        self.drag_tab_index = -1

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.drag_start_pos = event.pos()
            self.drag_tab_index = self.tabAt(self.drag_start_pos)
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.MouseButton.LeftButton):
            return super().mouseMoveEvent(event)
        
        if self.drag_tab_index == -1:
            return super().mouseMoveEvent(event)

        tab_widget = self.parent().widget(self.drag_tab_index)
        if tab_widget and hasattr(tab_widget, 'is_loading') and tab_widget.is_loading:
            return # Don't allow dragging while the tab is loading

        if (event.pos() - self.drag_start_pos).manhattanLength() < QApplication.startDragDistance():
            return super().mouseMoveEvent(event)

        drag = QDrag(self)
        mime_data = QMimeData()
        mime_data.setData("application/x-qbrowser-tab-index", QByteArray(str(self.drag_tab_index).encode()))
        drag.setMimeData(mime_data)

        # Use a simple dummy pixmap to avoid all painter errors
        pixmap = QPixmap(100, 30)
        pixmap.fill(QColor(53, 53, 53))
        painter = QPainter(pixmap)
        painter.setPen(Qt.GlobalColor.white)
        painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, self.tabText(self.drag_tab_index))
        painter.end()
        drag.setPixmap(pixmap)

        tab_rect = self.tabRect(self.drag_tab_index)
        if tab_rect.isValid():
            drag.setHotSpot(event.pos() - tab_rect.topLeft())
        else:
            drag.setHotSpot(event.pos())  # Fallback hotspot

        drop_action = drag.exec(Qt.DropAction.MoveAction)

        if drop_action == Qt.DropAction.IgnoreAction:
            self.tabDetached.emit(self.drag_tab_index, event.globalPosition().toPoint())
        
        self.drag_tab_index = -1
        super().mouseMoveEvent(event)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/x-qbrowser-tab-index"):
            event.acceptProposedAction()
        else:
            super().dragEnterEvent(event)

    def dropEvent(self, event):
        if not event.mimeData().hasFormat("application/x-qbrowser-tab-index"):
            return super().dropEvent(event)

        source_tab_bar = event.source()
        if not isinstance(source_tab_bar, DetachableTabBar):
            return super().dropEvent(event)

        source_widget = source_tab_bar.parent()
        dest_widget = self.parent()
        
        from_index = int(event.mimeData().data("application/x-qbrowser-tab-index"))
        to_index = self.tabAt(event.position().toPoint())

        if source_widget == dest_widget:
            if to_index == -1:
                to_index = self.count()
            
            self.moveTab(from_index, to_index)
            self.parent().setCurrentIndex(to_index)
            event.acceptProposedAction()
            return

        # Cross-widget drop
        tab_content = source_widget.widget(from_index)
        tab_title = source_widget.tabText(from_index)

        # Disconnect signals from the old browser
        old_browser = tab_content.browser
        if old_browser:
            try:
                tab_content.urlChanged.disconnect(old_browser.update_url)
                tab_content.titleChanged.disconnect()
                tab_content.loadStatusChanged.disconnect(old_browser.update_status_bar)
            except (TypeError, RuntimeError):
                pass

        # Remove from source widget. The page widget is not deleted.
        source_widget.removeTab(from_index)

        if to_index == -1:
            to_index = self.count()
        
        # Add to destination widget
        new_index = dest_widget.insertTab(to_index, tab_content, tab_title)
        dest_widget.setCurrentIndex(new_index)
        
        # Connect signals to the new browser
        new_browser = dest_widget.window()
        tab_content.browser = new_browser
        tab_content.urlChanged.connect(new_browser.update_url)
        tab_content.titleChanged.connect(lambda title, tab=tab_content: new_browser.update_title(title, tab))
        tab_content.loadStatusChanged.connect(new_browser.update_status_bar)

        # Close the source window if it has no more tabs
        if source_widget.count() == 0:
            source_widget.window().close()

        event.setDropAction(Qt.DropAction.MoveAction)
        event.accept()


class ProfileDialog(QDialog):
    """
    Dialog for selecting or creating a browser profile.
    """
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Select Profile")
        self.setMinimumWidth(400)
        self.selected_profile_path = None
        self.profiles_dir = "data"
        
        if not os.path.exists(self.profiles_dir):
            os.makedirs(self.profiles_dir)

        layout = QVBoxLayout(self)
        
        layout.addWidget(QLabel("Select a profile to launch:"))
        
        self.profile_list = QListWidget()
        self.profile_list.itemDoubleClicked.connect(self.accept_selection)
        layout.addWidget(self.profile_list)
        
        button_layout = QHBoxLayout()
        
        self.new_button = QPushButton("Create New")
        self.new_button.clicked.connect(self.create_new_profile)
        button_layout.addWidget(self.new_button)

        self.delete_button = QPushButton("Delete")
        self.delete_button.clicked.connect(self.delete_profile)
        button_layout.addWidget(self.delete_button)

        button_layout.addStretch()

        self.select_button = QPushButton("Launch")
        self.select_button.setDefault(True)
        self.select_button.clicked.connect(self.accept_selection)
        button_layout.addWidget(self.select_button)
        
        layout.addLayout(button_layout)
        
        self.load_profiles()
        self.update_button_states()

    def update_button_states(self):
        has_selection = len(self.profile_list.selectedItems()) > 0
        self.select_button.setEnabled(has_selection)
        self.delete_button.setEnabled(has_selection)

    def load_profiles(self):
        self.profile_list.clear()
        profiles = []
        for item in os.listdir(self.profiles_dir):
            if os.path.isdir(os.path.join(self.profiles_dir, item)) and item.startswith("browser_profile_"):
                profiles.append(item)
        
        for profile_dir_name in sorted(profiles):
            profile_name = profile_dir_name.replace("browser_profile_", "")
            list_item = QListWidgetItem(profile_name)
            list_item.setData(Qt.ItemDataRole.UserRole, os.path.join(self.profiles_dir, profile_dir_name))
            self.profile_list.addItem(list_item)
        
        if self.profile_list.count() > 0:
            self.profile_list.setCurrentRow(0)
        
        self.profile_list.itemSelectionChanged.connect(self.update_button_states)
        self.update_button_states()

    def create_new_profile(self):
        while True:
            profile_name, ok = QInputDialog.getText(self, "Create New Profile", "Enter a name for the new profile:")
            if not ok:
                return  # User cancelled
            
            if not profile_name.strip():
                QMessageBox.warning(self, "Invalid Name", "Profile name cannot be empty.")
                continue

            profile_path = os.path.join(self.profiles_dir, f"browser_profile_{profile_name.strip()}")
            if not os.path.exists(profile_path):
                try:
                    os.makedirs(profile_path)
                    self._install_default_extensions(profile_path)
                    self.load_profiles()
                    # Select the new profile
                    for i in range(self.profile_list.count()):
                        if self.profile_list.item(i).text() == profile_name.strip():
                            self.profile_list.setCurrentRow(i)
                            break
                    return
                except OSError as e:
                    QMessageBox.critical(self, "Error", f"Could not create profile directory: {e}")
                    return
            else:
                QMessageBox.warning(self, "Profile Exists", "A profile with that name already exists.")

    def delete_profile(self):
        selected_items = self.profile_list.selectedItems()
        if not selected_items:
            return
        
        item = selected_items[0]
        profile_name = item.text()
        profile_path = item.data(Qt.ItemDataRole.UserRole)

        reply = QMessageBox.question(self, "Confirm Deletion", f"Are you sure you want to delete the profile '{profile_name}'?\nThis will permanently delete all its data.",
                                     QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No)

        if reply == QMessageBox.StandardButton.Yes:
            try:
                shutil.rmtree(profile_path)
                self.load_profiles()
                self.update_button_states()
            except Exception as e:
                QMessageBox.critical(self, "Error", f"Could not delete profile: {e}")

    def accept_selection(self):
        selected_items = self.profile_list.selectedItems()
        if not selected_items:
            return
        
        item = selected_items[0]
        self.selected_profile_path = item.data(Qt.ItemDataRole.UserRole)
        self.accept()

    @staticmethod
    def _install_default_extensions(profile_path):
        """Copies default extensions from assets to a new profile directory."""
        source_extensions_dir = "assets/browser/extensions"
        if not os.path.isdir(source_extensions_dir):
            print(f"Default extensions directory not found: {source_extensions_dir}")
            return

        dest_extensions_dir = os.path.join(profile_path, "Default", "Extensions")
        
        try:
            os.makedirs(dest_extensions_dir, exist_ok=True)
            
            for item_name in os.listdir(source_extensions_dir):
                source_item_path = os.path.join(source_extensions_dir, item_name)
                dest_item_path = os.path.join(dest_extensions_dir, item_name)
                
                if os.path.isdir(source_item_path):
                    if not os.path.exists(dest_item_path):
                        shutil.copytree(source_item_path, dest_item_path)
                        print(f"Installed default extension '{item_name}' to profile.")
        except (OSError, IOError) as e:
            QMessageBox.warning(None, "Extension Installation Error",
                                f"Could not install default extensions: {e}")

    @staticmethod
    def get_profile_path(parent=None):
        profiles_dir = "data"
        if not os.path.exists(profiles_dir):
            os.makedirs(profiles_dir)
            
        profiles = [p for p in os.listdir(profiles_dir) if p.startswith("browser_profile_")]

        if not profiles:
            # First run experience: must create a profile to continue.
            while True:
                profile_name, ok = QInputDialog.getText(parent, "Create First Profile", "Welcome! Please create a profile to begin:")
                if not ok:
                    return None  # User cancelled initial creation, so exit.
                
                if not profile_name.strip():
                    QMessageBox.warning(parent, "Invalid Name", "Profile name cannot be empty.")
                    continue

                profile_path = os.path.join(profiles_dir, f"browser_profile_{profile_name.strip()}")
                
                if os.path.exists(profile_path):
                    QMessageBox.warning(parent, "Profile Exists", "A profile with that name already exists. Please choose another name.")
                    continue

                try:
                    os.makedirs(profile_path)
                    ProfileDialog._install_default_extensions(profile_path)
                    return profile_path  # Success!
                except OSError as e:
                    QMessageBox.critical(parent, "Error", f"Could not create profile directory: {e}")
                    return None
        else:
            # Profiles exist, show the manager dialog.
            dialog = ProfileDialog(parent)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                return dialog.selected_profile_path
            else:
                return None  # User closed the manager.


class Browser(QMainWindow):
    """
    Main browser window that wraps the Playwright browser class.
    """
    instances = []

    def __init__(self, initial_url=None, debug=False, detached_tab=None, profile_path=None):
        super().__init__()
        Browser.instances.append(self)
        self.setWindowTitle("SHMCamStudio Browser")
        self.setWindowIcon(QIcon('assets/logo.jpg'))
        self.resize(1024, 768)

        # Create a central widget and layout
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout(self.central_widget)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        
        # Store loaded extensions
        self.loaded_extensions = {}

        # Create toolbar
        self.toolbar = QToolBar()
        self.toolbar.setMovable(False)
        self.toolbar.setIconSize(QSize(24, 24))

        # Add back button
        self.back_button = QPushButton()
        self.back_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_ArrowBack))
        self.back_button.setToolTip("Back")
        self.back_button.clicked.connect(self.navigate_back)
        self.toolbar.addWidget(self.back_button)

        # Add forward button
        self.forward_button = QPushButton()
        self.forward_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_ArrowForward))
        self.forward_button.setToolTip("Forward")
        self.forward_button.clicked.connect(self.navigate_forward)
        self.toolbar.addWidget(self.forward_button)

        # Add reload button
        self.reload_button = QPushButton()
        self.reload_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_BrowserReload))
        self.reload_button.setToolTip("Reload (F5)")
        self.reload_button.clicked.connect(self.reload_page)
        self.toolbar.addWidget(self.reload_button)

        # Add home button
        self.home_button = QPushButton()
        self.home_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_DirHomeIcon))
        self.home_button.setToolTip("Home")
        self.home_button.clicked.connect(self.navigate_home)
        self.toolbar.addWidget(self.home_button)

        # Add URL input
        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("Enter URL...")
        self.url_input.returnPressed.connect(self.navigate_to_url)
        self.toolbar.addWidget(self.url_input)

        # The extension buttons will be inserted before this one, so we need its action.
        self.extensions_button = QPushButton()
        self.extensions_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_FileDialogDetailedView))
        self.extensions_button.setToolTip("Extensions")
        self.extensions_button.clicked.connect(self.show_extensions)
        self.extensions_button_action = self.toolbar.addWidget(self.extensions_button)

        """
        # Add Chrome URLs button with dropdown menu
        self.chrome_button = QPushButton()
        self.chrome_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_TitleBarMenuButton))
        self.chrome_button.setToolTip("Chrome URLs")
        self.chrome_menu = QMenu(self)
        
        # Add common chrome:// URLs to the menu
        chrome_urls = {
            "Extensions": "chrome://extensions/",
            "Settings": "chrome://settings/",
            "History": "chrome://history/",
            "Bookmarks": "chrome://bookmarks/",
            "Downloads": "chrome://downloads/",
            "Flags": "chrome://flags/",
            "Version": "chrome://version/",
            "GPU": "chrome://gpu/",
            "Network": "chrome://net-internals/",
            "Inspect": "chrome://inspect/",
            "Media": "chrome://media-internals/",
            "Components": "chrome://components/",
            "System": "chrome://system/",
            "Chrome URLs": "chrome://chrome-urls/"  # This shows all available chrome:// URLs
        }
        
        # Add a separator and debug options
        self.chrome_menu.addSeparator()
        debug_action = self.chrome_menu.addAction("Debug Chrome URLs")
        debug_action.triggered.connect(self.debug_chrome_urls)
        
        for name, url in chrome_urls.items():
            action = self.chrome_menu.addAction(name)
            action.triggered.connect(lambda checked, u=url: self.navigate_to_chrome_url(u))
        
        self.chrome_button.setMenu(self.chrome_menu)
        self.toolbar.addWidget(self.chrome_button)
        """
        
        # Add new window button
        self.new_window_button = QPushButton()
        self.new_window_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_FileIcon))
        self.new_window_button.setToolTip("New Window (Ctrl+W)")
        self.new_window_button.clicked.connect(self.open_new_window)
        self.toolbar.addWidget(self.new_window_button)

        if debug:
            self.debug_button = QPushButton()
            self.debug_button.setIcon(self._get_themed_icon(QStyle.StandardPixmap.SP_MessageBoxWarning))
            self.debug_button.setToolTip("Toggle Developer Tools")
            self.debug_button.clicked.connect(self.toggle_devtools)
            self.toolbar.addWidget(self.debug_button)

            # Add a test extension button to the toolbar
            self.test_extension_button = QPushButton("Test Extension")
            self.test_extension_button.setToolTip("Launch Test Extension")
            self.test_extension_button.clicked.connect(self.launch_test_extension)
            self.toolbar.addWidget(self.test_extension_button)

        self.layout.addWidget(self.toolbar)

        # Create tab widget
        self.tabs = QTabWidget()
        self.tab_bar = DetachableTabBar()
        self.tabs.setTabBar(self.tab_bar)
        self.tab_bar.tabDetached.connect(self.handle_tab_detached)
        
        self.tabs.setTabsClosable(True)
        # self.tabs.setMovable(True) # This is handled by our DetachableTabBar
        self.tabs.setDocumentMode(True)
        self.tabs.tabCloseRequested.connect(self.close_tab)
        self.tabs.currentChanged.connect(self.tab_changed)

        self.layout.addWidget(self.tabs)

        # Add status bar
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

        # Add a new tab button
        self.tabs.setCornerWidget(self._create_new_tab_button(), Qt.Corner.TopRightCorner)

        # Set the home URL from config
        config = configparser.ConfigParser()
        config.read('shmcamstudio.conf')
        self.home_url = config.get('Browser', 'home_url', fallback='https://www.google.com')

        # Centralized browser profile
        if not profile_path:
            # This should not happen if main is used, but as a fallback
            profile_path = "data/browser_profile_default"
        self.profile_dir = profile_path
        if not os.path.exists(self.profile_dir):
            os.makedirs(self.profile_dir)
        
        # Extensions must be in a specific subdirectory of the profile
        self.extensions_dir = os.path.join(self.profile_dir, "Default", "Extensions")
        if not os.path.exists(self.extensions_dir):
            os.makedirs(self.extensions_dir)
            
        self.profile = QWebEngineProfile(self.profile_dir, self)
        
        # Enable extension developer mode to allow loading unpacked extensions
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowWindowActivationFromJavaScript, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanPaste, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.ErrorPageEnabled, False)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.FocusOnNavigationEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
        # Enable access to chrome:// URLs and other special URLs
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
        
        # Additional settings to ensure chrome:// URLs work
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.ErrorPageEnabled, False)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.FocusOnNavigationEnabled, True)
        
        # Set developer mode flag in preferences
        self.profile.setHttpUserAgent(self.profile.httpUserAgent() + " ChromiumExtensionDevMode")
        
        # Enable Autofill feature for QWebEngine (using preferences instead of WebAttribute)
        # Note: AutoFillEnabled attribute is not available in this PyQt6 version
        
        # Create a directory for extension preferences if it doesn't exist
        prefs_dir = os.path.join(self.profile_dir, "Default")
        if not os.path.exists(prefs_dir):
            os.makedirs(prefs_dir)
            
        # Create or update the Preferences file to enable extension developer mode
        prefs_file = os.path.join(prefs_dir, "Preferences")
        prefs_data = {}
        if os.path.exists(prefs_file):
            try:
                with open(prefs_file, 'r') as f:
                    prefs_data = json.load(f)
            except json.JSONDecodeError:
                # If the file exists but is invalid JSON, start with an empty dict
                pass
        
        # Set extension developer mode preferences
        if 'extensions' not in prefs_data:
            prefs_data['extensions'] = {}
        prefs_data['extensions']['developer_mode'] = True
        
        # Enable unpacked extensions and user scripts
        if 'extensions' in prefs_data:
            # Allow loading unpacked extensions
            prefs_data['extensions']['allow_file_access'] = True
            # Allow user scripts (required by some extensions like Lovense)
            prefs_data['extensions']['allow_user_scripts'] = True
        
        # Enable Autofill in preferences
        if 'autofill' not in prefs_data:
            prefs_data['autofill'] = {}
        prefs_data['autofill']['enabled'] = True
        
        # Enable additional features for extensions
        if 'browser' not in prefs_data:
            prefs_data['browser'] = {}
        if 'enabled_labs_experiments' not in prefs_data['browser']:
            prefs_data['browser']['enabled_labs_experiments'] = []
        
        # Add Autofill experiments if not already present
        autofill_experiments = [
            "enable-autofill-credit-card-upload",
            "enable-autofill-credit-card-authentication",
            "enable-autofill-address-save-prompts",
            "enable-experimental-web-platform-features"
        ]
        
        for experiment in autofill_experiments:
            if experiment not in prefs_data['browser']['enabled_labs_experiments']:
                prefs_data['browser']['enabled_labs_experiments'].append(experiment)
        
        # Write the updated preferences back to the file
        with open(prefs_file, 'w') as f:
            json.dump(prefs_data, f, indent=2)
        
        # Check for Lovense extension and configure it specifically
        lovense_dir = os.path.join(self.extensions_dir, "lovense")
        if os.path.exists(lovense_dir):
            print("Lovense extension found, applying special configuration...")
            
            # Create secure preferences file if it doesn't exist
            secure_prefs_file = os.path.join(self.profile_dir, "Default", "Secure Preferences")
            secure_prefs_data = {}
            
            if os.path.exists(secure_prefs_file):
                try:
                    with open(secure_prefs_file, 'r') as f:
                        secure_prefs_data = json.load(f)
                except:
                    secure_prefs_data = {}
            
            # Ensure extensions section exists
            if 'extensions' not in secure_prefs_data:
                secure_prefs_data['extensions'] = {}
            
            # Add settings for Lovense
            if 'settings' not in secure_prefs_data['extensions']:
                secure_prefs_data['extensions']['settings'] = {}
            
            # Set Lovense as trusted
            secure_prefs_data['extensions']['settings']["lovense"] = {
                "ack_external_install_prompt": True,
                "installed_by_default": False,
                "installed_by_oem": False,
                "installed_by_policy": False,
                "mark_acknowledged_external_install": True,
                "was_installed_by_enterprise_policy": False
            }
            
            # Write secure preferences
            with open(secure_prefs_file, 'w') as f:
                json.dump(secure_prefs_data, f, indent=2)
            
            # Create Local State file with extension settings
            local_state_file = os.path.join(self.profile_dir, "Local State")
            local_state_data = {}
            
            if os.path.exists(local_state_file):
                try:
                    with open(local_state_file, 'r') as f:
                        local_state_data = json.load(f)
                except:
                    local_state_data = {}
            
            # Ensure extensions section exists
            if 'extensions' not in local_state_data:
                local_state_data['extensions'] = {}
            
            # Enable developer mode in local state
            local_state_data['extensions']['ui'] = {
                "developer_mode": True
            }
            
            # Write local state
            with open(local_state_file, 'w') as f:
                json.dump(local_state_data, f, indent=2)
            
            print("Lovense extension configuration complete")
            
        # Install URL scheme handlers
        self.extension_scheme_handler = ExtensionSchemeHandler(self.extensions_dir)
        # Reinstall URL scheme handlers
        self.profile.installUrlSchemeHandler(b"qextension", self.extension_scheme_handler)
        
        # Re-add the chrome.runtime API script to the profile's scripts
        #self.runtime_api_script = create_runtime_api_script()
        #self.profile.scripts().insert(self.runtime_api_script)
        
        # Re-register the RuntimeBridge with the QWebChannel
        self.web_channel = QWebChannel(self)
        self.runtime_bridge = RuntimeBridge(self, self.extensions_dir, self)
        self.web_channel.registerObject("runtimeBridge", self.runtime_bridge)
        
        # Initialize the content script injector
        self.content_script_injector = ContentScriptInjector(self.extensions_dir)
        
        # Re-register chrome:// protocol with Qt
        QWebEngineProfile.defaultProfile().setUrlRequestInterceptor(
            ChromeUrlInterceptor(self)
        )
        
        # Register chrome:// protocol interceptor
        QWebEngineProfile.defaultProfile().setUrlRequestInterceptor(
            ChromeUrlInterceptor(self)
        )
        
        # Create and set up the RuntimeBridge for chrome.runtime API emulation
        #self.runtime_bridge = RuntimeBridge(self, self.extensions_dir, self)
        
        # Create a QWebChannel to communicate with JavaScript
        #self.web_channel = QWebChannel(self)
        #self.web_channel.registerObject("runtimeBridge", self.runtime_bridge)
        
        # Add the chrome.runtime API script to the profile's scripts
        self.runtime_api_script = create_runtime_api_script()
        self.profile.scripts().insert(self.runtime_api_script)
        
        
        if detached_tab:
            widget, title = detached_tab
            index = self.tabs.addTab(widget, title)
            self.tabs.setCurrentIndex(index)
            widget.browser = self
            widget.urlChanged.connect(self.update_url)
            widget.titleChanged.connect(lambda t, tab=widget: self.update_title(t, tab))
            widget.loadStatusChanged.connect(self.update_status_bar)
            if hasattr(widget, 'web_view'):
                self.update_title(widget.web_view.title(), widget)
                self.update_url(widget.web_view.url().toString())
        else:
            # Create a new tab on startup
            url_to_open = initial_url if initial_url is not None else self.home_url
            self.new_page(url_to_open)

        # Set up keyboard shortcuts
        self._setup_shortcuts()

        # Store Playwright objects
        self.playwright = None
        self.playwright_browser = None
        self.browser_contexts = {}
        self.dev_tools_windows = []
        self.open_extension_popups = []
        self.extension_actions = []

        self.update_extension_buttons()

    
    def closeEvent(self, event):
        if self in Browser.instances:
            Browser.instances.remove(self)
        super().closeEvent(event)
    
    def _get_themed_icon(self, standard_pixmap, color="white"):
        """Create a themed icon from a standard pixmap."""
        icon = self.style().standardIcon(standard_pixmap)
        pixmap = icon.pixmap(self.toolbar.iconSize())
        painter = QPainter(pixmap)
        painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn)
        painter.fillRect(pixmap.rect(), QColor(color))
        painter.end()
        return QIcon(pixmap)
    
    def _create_new_tab_button(self):
        """Create a button for adding new tabs."""
        button = QPushButton("+")
        button.setToolTip("New Tab")
        button.clicked.connect(lambda: self.new_page(self.home_url))
        return button
    
    def _setup_shortcuts(self):
        """Set up keyboard shortcuts."""
        # Ctrl+T: New Tab
        new_tab_shortcut = QAction("New Tab", self)
        new_tab_shortcut.setShortcut(QKeySequence("Ctrl+T"))
        new_tab_shortcut.triggered.connect(lambda: self.new_page(self.home_url))
        self.addAction(new_tab_shortcut)

        # Ctrl+Tab: Cycle Tabs
        cycle_tabs_shortcut = QAction("Cycle Tabs", self)
        cycle_tabs_shortcut.setShortcut(QKeySequence("Ctrl+Tab"))
        cycle_tabs_shortcut.triggered.connect(self.cycle_tabs)
        self.addAction(cycle_tabs_shortcut)

        # Ctrl+W: New Window
        new_window_shortcut = QAction("New Window", self)
        new_window_shortcut.setShortcut(QKeySequence("Ctrl+W"))
        new_window_shortcut.triggered.connect(self.open_new_window)
        self.addAction(new_window_shortcut)

        # Ctrl+L: Focus URL bar
        focus_url_shortcut = QAction("Focus URL", self)
        focus_url_shortcut.setShortcut(QKeySequence("Ctrl+L"))
        focus_url_shortcut.triggered.connect(self.url_input.setFocus)
        self.addAction(focus_url_shortcut)

        # F5: Reload page
        reload_shortcut = QAction("Reload", self)
        reload_shortcut.setShortcut(QKeySequence("F5"))
        reload_shortcut.triggered.connect(self.reload_page)
        self.addAction(reload_shortcut)
    
    def cycle_tabs(self):
        """Cycle through the open tabs."""
        if self.tabs.count() > 1:
            next_index = (self.tabs.currentIndex() + 1) % self.tabs.count()
            self.tabs.setCurrentIndex(next_index)

    def on_tab_removed(self):
        """Close the window if the last tab is removed."""
        if self.tabs.count() == 0:
            self.close()

    def handle_tab_detached(self, index, pos):
        """Handle a tab being detached to a new window."""
        if index < 0 or index >= self.tabs.count():
            return

        widget = self.tabs.widget(index)
        title = self.tabs.tabText(index)

        try:
            widget.urlChanged.disconnect(self.update_url)
            widget.titleChanged.disconnect()
            widget.loadStatusChanged.disconnect(self.update_status_bar)
        except (TypeError, RuntimeError):
            pass

        # Create a new browser instance, which will take ownership of the widget.
        new_browser = Browser(
            debug=("--debug" in sys.argv),
            detached_tab=(widget, title),
            profile_path=self.profile_dir
        )
        
        new_browser.setGeometry(self.geometry())
        new_browser.move(pos - QPoint(new_browser.width() // 4, 10))
        new_browser.show()

        # Now that the widget is safely reparented, remove the tab from the old browser.
        # The on_tab_removed method will handle closing the window if it's empty.
        self.tabs.removeTab(index)
        self.on_tab_removed()

    def new_page(self, url="about:blank"):
        """Create a new browser tab with the specified URL."""
        # Create a new tab
        tab = BrowserTab(parent=self.tabs, url=url, profile=self.profile)
        # Store a reference to the Browser instance
        tab.browser = self
        tab.urlChanged.connect(self.update_url)
        tab.titleChanged.connect(lambda title, tab=tab: self.update_title(title, tab))
        tab.loadStatusChanged.connect(self.update_status_bar)
        
        # Add the tab to the tab widget
        index = self.tabs.addTab(tab, "New Tab")
        self.tabs.setCurrentIndex(index)
        
        return tab
    
    def close_tab(self, index):
        """Close the tab at the specified index."""
        if index < 0 or index >= self.tabs.count():
            return
        
        # Get the tab widget and schedule it for deletion.
        tab = self.tabs.widget(index)
        if tab:
            tab.deleteLater()
        
        # Remove the tab from the tab bar.
        self.tabs.removeTab(index)
        self.on_tab_removed()
    
    def tab_changed(self, index):
        """Handle tab change events."""
        if index < 0:
            return
        
        # Update the URL input
        tab = self.tabs.widget(index)
        if hasattr(tab, 'url'):
            self.url_input.setText(tab.url)
        self.update_status_bar()
    
    def update_url(self, url):
        """Update the URL input when the page URL changes."""
        self.url_input.setText(url)
        
        # Update the tab's stored URL
        current_tab = self.tabs.currentWidget()
        if current_tab:
            current_tab.url = url
    
    def update_title(self, title, tab):
        """Update the tab title when the page title changes."""
        index = self.tabs.indexOf(tab)
        if index >= 0:
            self.tabs.setTabText(index, title or "New Tab")
    
    def navigate_to_url(self):
        """Navigate to the URL in the URL input."""
        url = self.url_input.text().strip()
        
        # Add http:// if no protocol is specified
        if url and not url.startswith(("http://", "https://", "file://", "about:", "chrome://")):
            url = "http://" + url
        
        # Navigate the current tab
        current_tab = self.tabs.currentWidget()
        if current_tab:
            current_tab.navigate(url)
    
    def navigate_home(self):
        """Navigate to the home URL."""
        current_tab = self.tabs.currentWidget()
        if current_tab:
            current_tab.navigate(self.home_url)
            
    def navigate_to_chrome_url(self, url):
        """Navigate to a chrome:// URL."""
        print(f"Navigating to chrome URL: {url}")
        
        # Create a new tab specifically for chrome:// URLs
        tab = self.new_page("about:blank")
        
        # Ensure the tab is using our custom ChromeWebEnginePage
        if hasattr(tab, 'web_view'):
            # If the page isn't already a ChromeWebEnginePage, replace it
            if not isinstance(tab.web_view.page(), ChromeWebEnginePage):
                page = ChromeWebEnginePage(self.profile, tab.web_view)
                tab.web_view.setPage(page)
            
            # Now navigate to the chrome:// URL
            tab.web_view.load(QUrl(url))
            print(f"Created new tab for chrome URL: {url}")

    def navigate_back(self):
        """Navigate back in the current tab."""
        current_tab = self.tabs.currentWidget()
        if current_tab and hasattr(current_tab, 'web_view'):
            current_tab.web_view.back()

    def navigate_forward(self):
        """Navigate forward in the current tab."""
        current_tab = self.tabs.currentWidget()
        if current_tab and hasattr(current_tab, 'web_view'):
            current_tab.web_view.forward()

    def reload_page(self):
        """Reload the current tab."""
        current_tab = self.tabs.currentWidget()
        if current_tab and hasattr(current_tab, 'web_view'):
            current_tab.web_view.reload()
    
    def open_new_window(self):
        """Open a new browser window."""
        new_browser = Browser(debug="--debug" in sys.argv, profile_path=self.profile_dir)
        new_browser.show()
    
    def toggle_devtools(self):
        """Opens the remote developer tools URL in a new window or shows extension debug info."""
        # Create a dialog to show debug options
        debug_dialog = QDialog(self)
        debug_dialog.setWindowTitle("Developer Tools")
        debug_dialog.resize(1024, 768)
        
        layout = QVBoxLayout()
        
        # Add tabs for different debug options
        tabs = QTabWidget()
        
        # Tab 1: Remote DevTools
        remote_tab = QWidget()
        remote_layout = QVBoxLayout(remote_tab)
        web_view = QWebEngineView()
        web_view.load(QUrl("http://localhost:9222"))
        remote_layout.addWidget(web_view)
        
        # Tab 2: Extension Debug Info
        ext_tab = QWidget()
        ext_layout = QVBoxLayout(ext_tab)
        
        # Add a text area to show preferences file content
        prefs_label = QLabel("Preferences File Content:")
        ext_layout.addWidget(prefs_label)
        
        prefs_text = QTextEdit()
        prefs_text.setReadOnly(True)
        ext_layout.addWidget(prefs_text)
        
        # Add a button to refresh the preferences content
        refresh_button = QPushButton("Refresh Preferences")
        ext_layout.addWidget(refresh_button)
        
        # Add a button to force enable developer mode
        force_button = QPushButton("Force Enable Developer Mode")
        ext_layout.addWidget(force_button)
        
        # Add a button specifically for fixing the Lovense extension
        lovense_button = QPushButton("Fix Lovense Extension")
        ext_layout.addWidget(lovense_button)
        
        # Function to load and display preferences
        def load_preferences():
            prefs_file = os.path.join(self.profile_dir, "Default", "Preferences")
            if os.path.exists(prefs_file):
                try:
                    with open(prefs_file, 'r') as f:
                        prefs_data = json.load(f)
                    
                    # Format the JSON for better readability
                    formatted_json = json.dumps(prefs_data, indent=2)
                    prefs_text.setText(formatted_json)
                    
                    # Highlight extension developer mode status
                    ext_dev_mode = prefs_data.get('extensions', {}).get('developer_mode', False)
                    allow_file_access = prefs_data.get('extensions', {}).get('allow_file_access', False)
                    allow_user_scripts = prefs_data.get('extensions', {}).get('allow_user_scripts', False)
                    
                    status_text = f"Extension Developer Mode: {ext_dev_mode}\n"
                    status_text += f"Allow File Access: {allow_file_access}\n"
                    status_text += f"Allow User Scripts: {allow_user_scripts}\n"
                    
                    QMessageBox.information(debug_dialog, "Extension Settings Status", status_text)
                    
                except Exception as e:
                    prefs_text.setText(f"Error loading preferences: {e}")
            else:
                prefs_text.setText("Preferences file not found")
        
        # Function to force enable developer mode
        def force_developer_mode():
            prefs_file = os.path.join(self.profile_dir, "Default", "Preferences")
            if os.path.exists(prefs_file):
                try:
                    with open(prefs_file, 'r') as f:
                        prefs_data = json.load(f)
                    
                    # Ensure extensions section exists
                    if 'extensions' not in prefs_data:
                        prefs_data['extensions'] = {}
                    
                    # Force enable all developer mode settings
                    prefs_data['extensions']['developer_mode'] = True
                    prefs_data['extensions']['allow_file_access'] = True
                    prefs_data['extensions']['allow_user_scripts'] = True
                    
                    # Write back to file
                    with open(prefs_file, 'w') as f:
                        json.dump(prefs_data, f, indent=2)
                    
                    # Reload the profile
                    self.reload_profile_and_tabs()
                    
                    QMessageBox.information(debug_dialog, "Success",
                                           "Developer mode forcefully enabled. Profile reloaded.")
                    
                    # Refresh the displayed preferences
                    load_preferences()
                    
                except Exception as e:
                    QMessageBox.critical(debug_dialog, "Error", f"Could not force developer mode: {e}")
            else:
                QMessageBox.warning(debug_dialog, "Error", "Preferences file not found")
        
        # Function to specifically fix the Lovense extension
        def fix_lovense_extension():
            # Check if Lovense extension exists
            lovense_dir = os.path.join(self.extensions_dir, "lovense")
            if not os.path.exists(lovense_dir):
                QMessageBox.warning(debug_dialog, "Error", "Lovense extension not found in extensions directory")
                return
                
            try:
                # 1. Update preferences file with specific settings for Lovense
                prefs_file = os.path.join(self.profile_dir, "Default", "Preferences")
                if os.path.exists(prefs_file):
                    with open(prefs_file, 'r') as f:
                        prefs_data = json.load(f)
                    
                    # Ensure extensions section exists
                    if 'extensions' not in prefs_data:
                        prefs_data['extensions'] = {}
                    
                    # Force enable all developer mode settings
                    prefs_data['extensions']['developer_mode'] = True
                    prefs_data['extensions']['allow_file_access'] = True
                    prefs_data['extensions']['allow_user_scripts'] = True
                    
                    # Add specific settings for Lovense extension
                    if 'settings' not in prefs_data['extensions']:
                        prefs_data['extensions']['settings'] = {}
                    
                    # Get Lovense extension ID (directory name)
                    lovense_id = "lovense"
                    
                    # Set specific permissions for Lovense
                    prefs_data['extensions']['settings'][lovense_id] = {
                        "active_permissions": {
                            "api": ["storage", "unlimitedStorage", "userScripts", "desktopCapture"],
                            "explicit_host": ["<all_urls>"],
                            "manifest_permissions": [],
                            "scriptable_host": ["<all_urls>"]
                        },
                        "granted_permissions": {
                            "api": ["storage", "unlimitedStorage", "userScripts", "desktopCapture"],
                            "explicit_host": ["<all_urls>"],
                            "manifest_permissions": [],
                            "scriptable_host": ["<all_urls>"]
                        },
                        "location": 1,  # 1 = unpacked extension
                        "runtime_allowed_hosts": ["*://*.lovense.com/*", "*://localhost/*"],
                        "runtime_blocked_hosts": []
                    }
                    
                    # Write back to file
                    with open(prefs_file, 'w') as f:
                        json.dump(prefs_data, f, indent=2)
                
                # 2. Create a secure_preferences file if it doesn't exist
                secure_prefs_file = os.path.join(self.profile_dir, "Default", "Secure Preferences")
                secure_prefs_data = {}
                
                if os.path.exists(secure_prefs_file):
                    try:
                        with open(secure_prefs_file, 'r') as f:
                            secure_prefs_data = json.load(f)
                    except:
                        secure_prefs_data = {}
                
                # Ensure extensions section exists
                if 'extensions' not in secure_prefs_data:
                    secure_prefs_data['extensions'] = {}
                
                # Add settings for Lovense
                if 'settings' not in secure_prefs_data['extensions']:
                    secure_prefs_data['extensions']['settings'] = {}
                
                # Set Lovense as trusted
                secure_prefs_data['extensions']['settings']["lovense"] = {
                    "ack_external_install_prompt": True,
                    "installed_by_default": False,
                    "installed_by_oem": False,
                    "installed_by_policy": False,
                    "mark_acknowledged_external_install": True,
                    "was_installed_by_enterprise_policy": False
                }
                
                # Write secure preferences
                with open(secure_prefs_file, 'w') as f:
                    json.dump(secure_prefs_data, f, indent=2)
                
                # 3. Create Local State file with extension settings
                local_state_file = os.path.join(self.profile_dir, "Local State")
                local_state_data = {}
                
                if os.path.exists(local_state_file):
                    try:
                        with open(local_state_file, 'r') as f:
                            local_state_data = json.load(f)
                    except:
                        local_state_data = {}
                
                # Ensure extensions section exists
                if 'extensions' not in local_state_data:
                    local_state_data['extensions'] = {}
                
                # Enable developer mode in local state
                local_state_data['extensions']['ui'] = {
                    "developer_mode": True
                }
                
                # Write local state
                with open(local_state_file, 'w') as f:
                    json.dump(local_state_data, f, indent=2)
                
                # Reload the profile
                self.reload_profile_and_tabs()
                
                QMessageBox.information(debug_dialog, "Success",
                                       "Lovense extension has been fixed and profile reloaded.")
                
                # Refresh the displayed preferences
                load_preferences()
                
            except Exception as e:
                QMessageBox.critical(debug_dialog, "Error", f"Could not fix Lovense extension: {e}")
        
        # Connect buttons to functions
        refresh_button.clicked.connect(load_preferences)
        force_button.clicked.connect(force_developer_mode)
        lovense_button.clicked.connect(fix_lovense_extension)
        
        # Load preferences initially
        load_preferences()
        
        # Add tabs to the tab widget
        tabs.addTab(remote_tab, "Remote DevTools")
        tabs.addTab(ext_tab, "Extension Debug")
        
        layout.addWidget(tabs)
        debug_dialog.setLayout(layout)
        debug_dialog.show()
        
        self.dev_tools_windows.append(debug_dialog)
        debug_dialog.finished.connect(lambda: self.dev_tools_windows.remove(debug_dialog))

    def update_status_bar(self):
        """Update the status bar based on the current tab's state."""
        current_tab = self.tabs.currentWidget()
        if not current_tab:
            self.status_bar.showMessage("Ready")
            return
        
        if current_tab.is_loading:
            self.status_bar.showMessage("Loading...")
        else:
            self.status_bar.showMessage("Ready")

    def reload_profile_and_tabs(self):
        """Reloads the browser profile and recreates pages in all tabs."""
        print("Reloading profile to apply extension changes...")

        # 1. Save the URLs and current index of all open tabs
        urls = []
        current_index = self.tabs.currentIndex()
        for i in range(self.tabs.count()):
            tab = self.tabs.widget(i)
            if hasattr(tab, 'web_view') and tab.web_view.url().isValid():
                urls.append(tab.web_view.url().toString())
            else:
                urls.append("about:blank")

        # 2. Store the old profile to delete it later
        old_profile = self.profile
        old_profile.clearHttpCache()

        # 3. Clear all existing tabs. This removes references to pages using the old profile.
        self.tabs.clear()

        # 4. Create a new profile object. This forces a re-read from disk because
        #    the old one will be properly deleted.
        self.profile = QWebEngineProfile(self.profile_dir, self)
        
        # Re-enable extension developer mode and Autofill settings
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowWindowActivationFromJavaScript, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanPaste, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
        self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True)
        
        # Set developer mode flag in preferences
        self.profile.setHttpUserAgent(self.profile.httpUserAgent() + " ChromiumExtensionDevMode")
        
        # Re-apply extension developer mode settings to preferences file
        prefs_dir = os.path.join(self.profile_dir, "Default")
        prefs_file = os.path.join(prefs_dir, "Preferences")
        if os.path.exists(prefs_file):
            try:
                with open(prefs_file, 'r') as f:
                    prefs_data = json.load(f)
                
                # Ensure extension developer mode is enabled
                if 'extensions' not in prefs_data:
                    prefs_data['extensions'] = {}
                prefs_data['extensions']['developer_mode'] = True
                prefs_data['extensions']['allow_file_access'] = True
                prefs_data['extensions']['allow_user_scripts'] = True
                
                # Write the updated preferences back to the file
                with open(prefs_file, 'w') as f:
                    json.dump(prefs_data, f, indent=2)
                    
                # Check for Lovense extension and reconfigure it
                lovense_dir = os.path.join(self.extensions_dir, "lovense")
                if os.path.exists(lovense_dir):
                    print("Lovense extension found during reload, reapplying special configuration...")
                    
                    # Update specific settings for Lovense extension in preferences
                    if 'settings' not in prefs_data['extensions']:
                        prefs_data['extensions']['settings'] = {}
                    
                    # Set specific permissions for Lovense
                    prefs_data['extensions']['settings']["lovense"] = {
                        "active_permissions": {
                            "api": ["storage", "unlimitedStorage", "userScripts", "desktopCapture"],
                            "explicit_host": ["<all_urls>"],
                            "manifest_permissions": [],
                            "scriptable_host": ["<all_urls>"]
                        },
                        "granted_permissions": {
                            "api": ["storage", "unlimitedStorage", "userScripts", "desktopCapture"],
                            "explicit_host": ["<all_urls>"],
                            "manifest_permissions": [],
                            "scriptable_host": ["<all_urls>"]
                        },
                        "location": 1,  # 1 = unpacked extension
                        "runtime_allowed_hosts": ["*://*.lovense.com/*", "*://localhost/*"],
                        "runtime_blocked_hosts": []
                    }
                    
                    # Write updated preferences with Lovense settings
                    with open(prefs_file, 'w') as f:
                        json.dump(prefs_data, f, indent=2)
                    
                    # Update secure preferences
                    secure_prefs_file = os.path.join(self.profile_dir, "Default", "Secure Preferences")
                    secure_prefs_data = {}
                    
                    if os.path.exists(secure_prefs_file):
                        try:
                            with open(secure_prefs_file, 'r') as f:
                                secure_prefs_data = json.load(f)
                        except:
                            secure_prefs_data = {}
                    
                    # Ensure extensions section exists
                    if 'extensions' not in secure_prefs_data:
                        secure_prefs_data['extensions'] = {}
                    
                    # Add settings for Lovense
                    if 'settings' not in secure_prefs_data['extensions']:
                        secure_prefs_data['extensions']['settings'] = {}
                    
                    # Set Lovense as trusted
                    secure_prefs_data['extensions']['settings']["lovense"] = {
                        "ack_external_install_prompt": True,
                        "installed_by_default": False,
                        "installed_by_oem": False,
                        "installed_by_policy": False,
                        "mark_acknowledged_external_install": True,
                        "was_installed_by_enterprise_policy": False
                    }
                    
                    # Write secure preferences
                    with open(secure_prefs_file, 'w') as f:
                        json.dump(secure_prefs_data, f, indent=2)
                    
                    # Update Local State file
                    local_state_file = os.path.join(self.profile_dir, "Local State")
                    local_state_data = {}
                    
                    if os.path.exists(local_state_file):
                        try:
                            with open(local_state_file, 'r') as f:
                                local_state_data = json.load(f)
                        except:
                            local_state_data = {}
                    
                    # Ensure extensions section exists
                    if 'extensions' not in local_state_data:
                        local_state_data['extensions'] = {}
                    
                    # Enable developer mode in local state
                    local_state_data['extensions']['ui'] = {
                        "developer_mode": True
                    }
                    
                    # Write local state
                    with open(local_state_file, 'w') as f:
                        json.dump(local_state_data, f, indent=2)
                    
                    print("Lovense extension reconfiguration complete during reload")
            except Exception as e:
                print(f"Error updating preferences file during reload: {e}")
        
        self.profile.installUrlSchemeHandler(b"qextension", self.extension_scheme_handler)
        
        # 5. Re-create all tabs with the new profile
        if not urls: # Ensure there's at least one tab
            urls.append(self.home_url)
            
        for url in urls:
            self.new_page(url)
        
        # 6. Restore the previously active tab
        if current_index != -1 and current_index < len(urls):
            self.tabs.setCurrentIndex(current_index)
            
        # 7. Reinitialize the content script injector
        self.content_script_injector = ContentScriptInjector(self.extensions_dir)
        
        # 8. Schedule the old profile for deletion. This is crucial.
        old_profile.setParent(None)
        old_profile.deleteLater()

        print("Profile and tabs reloaded.")
        self.update_extension_buttons()

    def open_extension_popup_from_toolbar(self, ext_path):
        """Opens an extension's popup from a toolbar button click."""
        ext_name = os.path.basename(ext_path)
        manifest_path = os.path.join(ext_path, "manifest.json")

        if not os.path.exists(manifest_path):
            QMessageBox.warning(self, "Error", "manifest.json not found for this extension.")
            return

        try:
            with open(manifest_path, 'r', encoding='utf-8') as f:
                manifest = json.load(f)
        except Exception as e:
            QMessageBox.critical(self, "Error", f"Could not read manifest.json: {e}")
            return

        popup_path = None
        action = manifest.get('action') or manifest.get('browser_action') or manifest.get('page_action')
        if action and action.get('default_popup'):
            popup_path = action.get('default_popup')

        if not popup_path:
            # This should not happen if the button was created, but as a safeguard:
            QMessageBox.information(self, "No Popup", "This extension does not have a popup.")
            return

        popup_file = os.path.join(ext_path, popup_path)
        if not os.path.exists(popup_file):
            QMessageBox.warning(self, "Error", f"Popup file not found: {popup_path}")
            return

        # Open the popup in a new non-modal dialog
        popup_dialog = QDialog(self)
        popup_dialog.setWindowTitle(manifest.get("name", "Extension Popup"))
        layout = QVBoxLayout(popup_dialog)
        web_view = QWebEngineView()
        
        # The 'self' here is the Browser instance, so self.profile is correct
        page = QWebEnginePage(self.profile, web_view)
        web_view.setPage(page)
        
        # Set up QWebChannel for the page to enable chrome.runtime API
        page.setWebChannel(self.web_channel)
        
        # Explicitly initialize the QWebChannel with a script
        init_script = """
            // Create a global variable to track if transport is ready
            window.__qtWebChannelTransportReady = false;
            
            function initializeWebChannel() {
                if (typeof qt !== 'undefined' && qt.webChannelTransport) {
                    try {
                        // Check if QWebChannel is defined
                        if (typeof QWebChannel === 'undefined') {
                            console.log("QWebChannel is not defined yet, will retry later");
                            return false;
                        }
                        
                        new QWebChannel(qt.webChannelTransport, function(channel) {
                            window.channel = channel;
                            window.runtimeBridge = channel.objects.runtimeBridge;
                            window.__qtWebChannelTransportReady = true;
                            console.log("QWebChannel initialized successfully for extension popup");
                            
                            // Dispatch an event to notify that the channel is ready
                            document.dispatchEvent(new CustomEvent('webChannelReady', {
                                detail: { channel: channel }
                            }));
                            
                            // Also dispatch the chrome.runtime.initialized event
                            document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {
                                detail: { extensionId: "%s" }
                            }));
                        });
                        return true;
                    } catch (e) {
                        console.error("Error initializing QWebChannel:", e);
                        return false;
                    }
                } else {
                    console.log("qt.webChannelTransport not available yet, will retry later");
                    return false;
                }
            }
            
            // Try to initialize immediately
            if (!initializeWebChannel()) {
                // If it fails, set up a retry mechanism
                let retryCount = 0;
                const MAX_RETRIES = 50;
                
                const retryInterval = setInterval(function() {
                    console.log("Retrying QWebChannel initialization... (attempt " + (retryCount + 1) + " of " + MAX_RETRIES + ")");
                    if (initializeWebChannel() || retryCount >= MAX_RETRIES) {
                        clearInterval(retryInterval);
                        if (retryCount >= MAX_RETRIES) {
                            console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
                        }
                    }
                    retryCount++;
                }, 200);
            }
        """ % ext_name
        
        # Run the initialization script before loading the URL
        page.runJavaScript(init_script, lambda result: print("QWebChannel initialization script executed for popup"))

        popup_url = QUrl(f"qextension://{ext_name}/{popup_path}")
        web_view.load(popup_url)
        
        layout.addWidget(web_view)
        popup_dialog.setLayout(layout)
        popup_dialog.resize(400, 600)
        
        popup_dialog.show()
        
        # Keep a reference to prevent garbage collection
        self.open_extension_popups.append(popup_dialog)
        popup_dialog.finished.connect(lambda: self.open_extension_popups.remove(popup_dialog))

    
    def update_extension_buttons(self):
        """Scans for extensions and adds a button for each one with a popup to the main toolbar."""
        # Remove previous extension buttons
        for action in self.extension_actions:
            self.toolbar.removeAction(action)
        self.extension_actions.clear()

        if not self.extensions_dir or not os.path.exists(self.extensions_dir):
            return

        # refresh the loaded_extension list
        self.loaded_extensions = {}

        # Find all enabled extension directories
        for ext_name in sorted(os.listdir(self.extensions_dir)):
            if ext_name.endswith(".disabled"):
                continue

            ext_path = os.path.join(self.extensions_dir, ext_name)
            if not os.path.isdir(ext_path):
                continue

            manifest_path = os.path.join(ext_path, "manifest.json")
            if not os.path.exists(manifest_path):
                continue

            try:
                with open(manifest_path, 'r', encoding='utf-8') as f:
                    manifest = json.load(f)
            except Exception as e:
                print(f"Error loading manifest for {ext_name}: {e}")
                continue

            # Store the extension info
            self.loaded_extensions[ext_name] = {
                'path': ext_path,
                'manifest': manifest,
                'id': ext_name
            }

            # Check for a popup action
            popup_path = None
            action = manifest.get('action') or manifest.get('browser_action') or manifest.get('page_action')
            if action and action.get('default_popup'):
                popup_path = action.get('default_popup')

            if popup_path:
                # Create a button for this extension
                button = QPushButton()
                button.setFlat(True)
                button.setIconSize(QSize(22, 22))

                # Set the icon
                icon_path = None
                if manifest.get("icons"):
                    # Prefer a 24px or 16px icon for the toolbar
                    icon_path = manifest["icons"].get("24") or manifest["icons"].get("16") or next(iter(manifest["icons"].values()), None)
                
                if icon_path:
                    full_icon_path = os.path.join(ext_path, icon_path)
                    if os.path.exists(full_icon_path):
                        button.setIcon(QIcon(full_icon_path))
                
                button.setToolTip(manifest.get("name", ext_name))
                
                button.clicked.connect(partial(self.open_extension_popup_from_toolbar, ext_path))
                
                # Insert the button before the main "Extensions" button
                action = self.toolbar.insertWidget(self.extensions_button_action, button)
                self.extension_actions.append(action)


    def launch_test_extension(self):
        """Launch the test extension in a new tab."""
        # Check if the test extension is loaded
        print(self.loaded_extensions)
        if 'test-extension' not in self.loaded_extensions:
            QMessageBox.warning(self, "Extension Not Found",
                               "The test extension was not found. Make sure it's in the assets/browser/extensions directory.")
            return
        
        ext_info = self.loaded_extensions['test-extension']
        
        # First, launch the background page to ensure the background script is running
        self.launch_extension_background('test-extension')
        
        # Then open the popup in a new tab
        manifest = ext_info['manifest']
        popup_path = None
        
        # Check for popup in the manifest
        action = manifest.get('action') or manifest.get('browser_action') or manifest.get('page_action')
        if action and action.get('default_popup'):
            popup_path = action.get('default_popup')
        
        if popup_path:
            # Create a URL for the popup
            popup_url = f"qextension://test-extension/{popup_path}"
            
            # Open in a new tab
            tab = self.new_page(popup_url)
            self.tabs.setTabText(self.tabs.indexOf(tab), f"Test Extension: {manifest.get('name', 'Popup')}")
            
            print(f"Launched test extension popup: {popup_url}")
        else:
            QMessageBox.warning(self, "No Popup", "The test extension does not have a popup defined in its manifest.")
    
    def launch_extension_background(self, extension_id):
        """Launch an extension's background page or script in a hidden tab."""
        print(self.loaded_extensions)
        if extension_id not in self.loaded_extensions:
            print(f"Extension {extension_id} not found")
            return None
        
        ext_info = self.loaded_extensions[extension_id]
        manifest = ext_info['manifest']
        
        # Check for background page or script
        background = manifest.get('background', {})
        background_page = background.get('page')
        background_script = background.get('service_worker')
        
        if background_page:
            # Create a URL for the background page
            bg_url = f"qextension://{extension_id}/{background_page}"
            
            # Open in a new tab (could be hidden in a real implementation)
            tab = self.new_page(bg_url)
            self.tabs.setTabText(self.tabs.indexOf(tab), f"{extension_id} Background")
            
            # Ensure WebChannel is set up for this tab
            if hasattr(tab, 'web_view') and hasattr(tab.web_view, 'page'):
                page = tab.web_view.page()
                
                # First load QWebChannel.js content
                qwebchannel_path = os.path.join("assets/browser/js", "qwebchannel.js")
                qwebchannel_js = ""
                try:
                    with open(qwebchannel_path, 'r', encoding='utf-8') as f:
                        qwebchannel_js = f.read()
                except Exception as e:
                    print(f"Error loading QWebChannel.js: {e}")
                    qwebchannel_js = "console.error('Failed to load QWebChannel.js');"
                
                # Explicitly initialize the QWebChannel with a script that includes QWebChannel.js
                init_script = f"""
                    // Directly inject QWebChannel.js content
                    {qwebchannel_js}
                    
                    console.log("QWebChannel.js directly injected into background page");
                    
                    // Verify QWebChannel is defined
                    if (typeof QWebChannel === 'undefined') {{
                        console.error("QWebChannel is still undefined after direct injection!");
                    }} else {{
                        console.log("QWebChannel is successfully defined in global scope");
                    }}
                    
                    // Create a global variable to track if transport is ready
                    window.__qtWebChannelTransportReady = false;
                    
                    function initializeWebChannel() {{
                        if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                            try {{
                                // QWebChannel should be defined now
                                new QWebChannel(qt.webChannelTransport, function(channel) {{
                                    window.channel = channel;
                                    window.runtimeBridge = channel.objects.runtimeBridge;
                                    window.__qtWebChannelTransportReady = true;
                                    console.log("QWebChannel initialized successfully for background page");
                                    
                                    // Dispatch an event to notify that the channel is ready
                                    document.dispatchEvent(new CustomEvent('webChannelReady', {{
                                        detail: {{ channel: channel }}
                                    }}));
                                    
                                    // Also dispatch the chrome.runtime.initialized event
                                    document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {{
                                        detail: {{ extensionId: "{extension_id}" }}
                                    }}));
                                    
                                    // Make chrome.runtime available globally
                                    if (window.runtimeBridge) {{
                                        console.log("RuntimeBridge is available, initializing chrome.runtime API");
                                        // Expose chrome.runtime API
                                        if (typeof chrome === 'undefined') {{
                                            window.chrome = {{}};
                                        }}
                                        if (typeof chrome.runtime === 'undefined') {{
                                            chrome.runtime = {{}};
                                        }}
                                        
                                        // Define basic chrome.runtime API methods
                                        chrome.runtime.id = "{extension_id}";
                                        chrome.runtime.sendMessage = function(extensionId, message, options, callback) {{
                                            if (typeof extensionId !== 'string') {{
                                                callback = options;
                                                options = message;
                                                message = extensionId;
                                                extensionId = "{extension_id}";
                                            }}
                                            // Always pass an options parameter, even if it's empty
                                            if (typeof options === 'undefined') {{
                                                options = {{}};
                                            }}
                                            return window.runtimeBridge.sendMessage(extensionId, JSON.stringify(message), JSON.stringify(options));
                                        }};
                                    }}
                                }});
                                return true;
                            }} catch (e) {{
                                console.error("Error initializing QWebChannel:", e);
                                return false;
                            }}
                        }} else {{
                            console.log("qt.webChannelTransport not available yet, will retry later");
                            return false;
                        }}
                    }}
                    
                    // Create a global variable to track initialization attempts
                    window.__qwebchannel_init_attempts = 0;
                    
                    // Try to initialize immediately
                    if (!initializeWebChannel()) {{
                        // If it fails, set up a retry mechanism
                        const MAX_RETRIES = 50;
                        
                        const retryInterval = setInterval(function() {{
                            window.__qwebchannel_init_attempts++;
                            console.log("Retrying QWebChannel initialization... (attempt " + window.__qwebchannel_init_attempts + " of " + MAX_RETRIES + ")");
                            
                            // Check if qt object exists, if not, try to create it
                            if (typeof qt === 'undefined') {{
                                console.log("qt object is undefined, creating placeholder");
                                window.qt = {{}};
                            }}
                            
                            if (initializeWebChannel() || window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                                clearInterval(retryInterval);
                                if (window.__qwebchannel_init_attempts >= MAX_RETRIES) {{
                                    console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
                                }}
                            }}
                        }}, 200);
                    }}
                """
                
                # Run the initialization script
                page.runJavaScript(init_script, lambda result: print("QWebChannel initialization script executed for background page"))
            
            print(f"Launched extension background page: {bg_url}")
            return tab
        elif background_script:
            # For service workers, we need to create a special page that loads the script
            # This is a simplified approach - in a real implementation, you'd use a proper service worker container
            
            # Create a temporary HTML file that loads the background script
            temp_dir = os.path.join(self.profile_dir, "temp")
            os.makedirs(temp_dir, exist_ok=True)
            
            # Include WebChannel initialization in the HTML file
            temp_html_path = os.path.join(temp_dir, f"{extension_id}_background.html")
            with open(temp_html_path, 'w', encoding='utf-8') as f:
                f.write(f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{extension_id} Background</title>
    <script>
        // Create a global variable to track if transport is ready
        window.__qtWebChannelTransportReady = false;
        
        function initializeWebChannel() {{
            if (typeof qt !== 'undefined' && qt.webChannelTransport) {{
                try {{
                    // Check if QWebChannel is defined
                    if (typeof QWebChannel === 'undefined') {{
                        console.log("QWebChannel is not defined yet, will retry later");
                        return false;
                    }}
                    
                    new QWebChannel(qt.webChannelTransport, function(channel) {{
                        window.channel = channel;
                        window.runtimeBridge = channel.objects.runtimeBridge;
                        window.__qtWebChannelTransportReady = true;
                        console.log("QWebChannel initialized successfully for background script");
                        
                        // Dispatch an event to notify that the channel is ready
                        document.dispatchEvent(new CustomEvent('webChannelReady', {{
                            detail: {{ channel: channel }}
                        }}));
                        
                        // Also dispatch the chrome.runtime.initialized event
                        document.dispatchEvent(new CustomEvent('chrome.runtime.initialized', {{
                            detail: {{ extensionId: "{extension_id}" }}
                        }}));
                    }});
                    return true;
                }} catch (e) {{
                    console.error("Error initializing QWebChannel:", e);
                    return false;
                }}
            }} else {{
                console.log("qt.webChannelTransport not available yet, will retry later");
                return false;
            }}
        }}
        
        // Try to initialize immediately
        document.addEventListener('DOMContentLoaded', function() {{
            if (!initializeWebChannel()) {{
                // If it fails, set up a retry mechanism
                let retryCount = 0;
                const MAX_RETRIES = 50;
                
                const retryInterval = setInterval(function() {{
                    console.log("Retrying QWebChannel initialization... (attempt " + (retryCount + 1) + " of " + MAX_RETRIES + ")");
                    if (initializeWebChannel() || retryCount >= MAX_RETRIES) {{
                        clearInterval(retryInterval);
                        if (retryCount >= MAX_RETRIES) {{
                            console.error("Failed to initialize QWebChannel after " + MAX_RETRIES + " attempts");
                        }}
                    }}
                    retryCount++;
                }}, 200);
            }}
        }});
    </script>
</head>
<body>
    <h1>{extension_id} Background Script</h1>
    <p>This page loads the background script for the extension.</p>
    <script src="qextension://{extension_id}/{background_script}"></script>
</body>
</html>""")
            
            # Open the temporary HTML file in a new tab
            tab = self.new_page(f"file:///{temp_html_path}")
            self.tabs.setTabText(self.tabs.indexOf(tab), f"{extension_id} Background")
            
            # Ensure WebChannel is set up for this tab
            if hasattr(tab, 'web_view') and hasattr(tab.web_view, 'page'):
                page = tab.web_view.page()
                page.setWebChannel(self.web_channel)
            
            print(f"Launched extension background script: {background_script}")
            return tab
        else:
            print(f"Extension {extension_id} has no background page or script")
            return None
    
    def debug_chrome_urls(self):
        """Debug chrome:// URLs by showing information and testing navigation."""
        debug_dialog = QDialog(self)
        debug_dialog.setWindowTitle("Chrome URL Debug")
        debug_dialog.resize(600, 500)
        
        layout = QVBoxLayout(debug_dialog)
        
        # Add information about the current configuration
        info_text = QTextEdit()
        info_text.setReadOnly(True)
        
        # Collect debug information
        debug_info = [
            "Chrome URL Debug Information:",
            "----------------------------",
            f"Profile Path: {self.profile_dir}",
            f"User Agent: {self.profile.httpUserAgent()}",
            "WebEngine Settings:",
            f"  LocalContentCanAccessRemoteUrls: {self.profile.settings().testAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls)}",
            f"  ErrorPageEnabled: {self.profile.settings().testAttribute(QWebEngineSettings.WebAttribute.ErrorPageEnabled)}",
            f"  FocusOnNavigationEnabled: {self.profile.settings().testAttribute(QWebEngineSettings.WebAttribute.FocusOnNavigationEnabled)}",
            f"  AllowRunningInsecureContent: {self.profile.settings().testAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent)}",
            "Environment Variables:",
            f"  QTWEBENGINE_CHROMIUM_FLAGS: {os.environ.get('QTWEBENGINE_CHROMIUM_FLAGS', 'Not set')}",
            "----------------------------",
            "Instructions:",
            "1. Click 'Test Chrome URL' to open chrome://version/ in a new tab",
            "2. If it doesn't work, try restarting the browser with --debug flag",
            "3. Check if the custom ChromeWebEnginePage is being used correctly"
        ]
        
        info_text.setText("\n".join(debug_info))
        layout.addWidget(info_text)
        
        # Add buttons for testing
        button_layout = QHBoxLayout()
        
        test_button = QPushButton("Test Chrome URL")
        test_button.clicked.connect(lambda: self.navigate_to_chrome_url("chrome://version/"))
        button_layout.addWidget(test_button)
        
        close_button = QPushButton("Close")
        close_button.clicked.connect(debug_dialog.accept)
        button_layout.addWidget(close_button)
        
        layout.addLayout(button_layout)
        
        debug_dialog.exec()
    
    def show_extensions(self):
        """Show the extensions dialog."""
        dialog = ExtensionDialog(self, self.extensions_dir)
        dialog.exec()


class QPlaywrightBrowser:
    """
    A class that wraps the Playwright browser API and uses Qt6Browser internally.
    This class exposes the Playwright API while using the Qt6 browser for display.
    """
    def __init__(self):
        self.app = QApplication.instance() or QApplication(sys.argv)
        self.browser_ui = None
        self.playwright = None
        self.browser = None
        self.contexts = {}
        self.event_loop = None
    
    async def launch(self, **kwargs):
        """Launch a new browser instance."""
        # Initialize Playwright if not already done
        if not self.playwright:
            self.playwright = await async_playwright().start()
        
        # Create the Qt browser UI
        if not self.browser_ui:
            self.browser_ui = Browser()
            self.browser_ui.show()
        
        # Launch the actual Playwright browser (hidden)
        # We'll use this for API compatibility but display in Qt
        self.browser = await self.playwright.chromium.launch(
            headless=True,  # Run headless since we're displaying in Qt
            args = [
               '--headless=new',
               '--enable-features=Autofill',
               ]+os.environ.get('QTWEBENGINE_CHROMIUM_FLAGS').split(),
            **kwargs
        )
        #
        
        # Create a default context
        default_context = await self.browser.new_context()
        self.contexts["default"] = default_context
        
        return self
    
    async def new_context(self, **kwargs):
        """Create a new browser context."""
        if not self.browser:
            await self.launch()
        
        # Create a new context in the Playwright browser
        context_id = f"context_{len(self.contexts)}"
        context = await self.browser.new_context(**kwargs)
        self.contexts[context_id] = context
        
        # Return a wrapper that provides both Playwright API and Qt UI
        return QPlaywrightBrowserContext(self, context, context_id)
    
    async def new_page(self, url="about:blank"):
        """Create a new page in the default context."""
        if "default" not in self.contexts:
            await self.launch()
        
        # Create a new page in the Playwright browser
        pw_page = await self.contexts["default"].new_page()
        await pw_page.goto(url)
        
        # Create a new tab in the Qt browser
        qt_tab = self.browser_ui.new_page(url)
        qt_tab.page = pw_page
        
        # Return a wrapper that provides both Playwright API and Qt UI
        return QPlaywrightPage(pw_page, qt_tab)
    
    async def close(self):
        """Close the browser."""
        # Close all Playwright contexts and browser
        if self.browser:
            for context_id, context in self.contexts.items():
                await context.close()
            await self.browser.close()
            self.contexts = {}
            self.browser = None
        
        # Close the Qt browser UI
        if self.browser_ui:
            self.browser_ui.close()
            self.browser_ui = None
        
        # Close Playwright
        if self.playwright:
            await self.playwright.stop()
            self.playwright = None
    
    def run(self):
        """Run the application event loop."""
        return self.app.exec()
    
    def run_async(self, coro):
        """Run an async coroutine in the Qt event loop."""
        if not self.event_loop:
            self.event_loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self.event_loop)
        
        # Create a QTimer to process asyncio events
        timer = QTimer()
        timer.timeout.connect(lambda: self.event_loop.run_until_complete(asyncio.sleep(0)))
        timer.start(10)  # 10ms interval
        
        # Run the coroutine
        future = asyncio.run_coroutine_threadsafe(coro, self.event_loop)
        return future


class QPlaywrightBrowserContext:
    """
    A wrapper around a Playwright BrowserContext that provides both
    Playwright API and Qt UI integration.
    """
    def __init__(self, qbrowser, context, context_id):
        self.qbrowser = qbrowser
        self.context = context
        self.context_id = context_id
        self.pages = []
    
    async def new_page(self):
        """Create a new page in this context."""
        # Create a new page in the Playwright context
        pw_page = await self.context.new_page()
        
        # Create a new tab in the Qt browser
        qt_tab = self.qbrowser.browser_ui.new_page("about:blank")
        qt_tab.page = pw_page
        
        # Create a wrapper page
        page = QPlaywrightPage(pw_page, qt_tab)
        self.pages.append(page)
        
        return page
    
    async def close(self):
        """Close this context."""
        # Close all pages
        for page in self.pages:
            await page.close()
        
        # Close the Playwright context
        await self.context.close()
        
        # Remove from the qbrowser contexts
        if self.context_id in self.qbrowser.contexts:
            del self.qbrowser.contexts[self.context_id]


class QPlaywrightPage:
    """
    A wrapper around a Playwright Page that provides both
    Playwright API and Qt UI integration.
    """
    def __init__(self, pw_page, qt_tab):
        self.pw_page = pw_page
        self.qt_tab = qt_tab
        
        # Forward Playwright page methods and properties
        self.goto = pw_page.goto
        self.click = pw_page.click
        self.fill = pw_page.fill
        self.type = pw_page.type
        self.press = pw_page.press
        self.wait_for_selector = pw_page.wait_for_selector
        self.wait_for_navigation = pw_page.wait_for_navigation
        self.wait_for_load_state = pw_page.wait_for_load_state
        self.evaluate = pw_page.evaluate
        self.screenshot = pw_page.screenshot
        self.content = pw_page.content
        self.title = pw_page.title
        self.url = pw_page.url
    
    async def close(self):
        """Close this page."""
        # Close the Playwright page
        await self.pw_page.close()
        
        # Close the Qt tab
        # We need to use the Qt event loop for this
        index = self.qt_tab.parent.indexOf(self.qt_tab)
        if index >= 0:
            self.qt_tab.parent.close_tab(index)


async def main_async():
    """Async main function to run the browser."""
    browser = QPlaywrightBrowser()
    await browser.launch()
    
    # Open a page
    url = "https://www.google.com"
    if len(sys.argv) > 1:
        url = sys.argv[1]
    
    await browser.new_page(url)
    
    # Keep the browser running
    while True:
        await asyncio.sleep(0.1)


def main():
    """Main function to run the browser as a standalone application."""
   
    # Enable Autofill features and extension developer mode via environment variables
    chromium_flags = "--enable-features=AutofillEnableAccountWalletStorage,AutofillAddressProfileSavePrompt,AutofillCreditCardUpload,AutofillEnableToolbarStatusChip,AutofillKeyboardAccessory,AutofillShowAllSuggestionsOnPrefsCheckout,AutofillShowTypePredictions,AutofillUpstream,PasswordGeneration,PasswordGenerationBottomSheetUI,PasswordGenerationExperiment,ExtensionsToolbarMenu,ChromeUIDebugTools --extensions-on-chrome-urls --allow-file-access-from-files --allow-running-insecure-content --enable-user-scripts --allow-universal-access-from-files --disable-web-security --disable-site-isolation-trials --allow-insecure-localhost --ignore-certificate-errors --ignore-urlfetcher-cert-requests --disable-features=BlockInsecurePrivateNetworkRequests"
    
    # Always add extension developer mode flags
    #chromium_flags += " --load-extension=assets/browser/extensions --force-dev-mode-highlighting"
    chromium_flags += " --force-dev-mode-highlighting"

    # Explicitly enable chrome:// URLs
    chromium_flags += " --enable-chrome-urls --enable-features=ChromeUIDebugTools"
    
    # Chrome scheme is already registered in register_url_schemes()
    
    os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = chromium_flags
    
    debug_mode = "--debug" in sys.argv

    #Print environment variables for debugging and set remote debug where needed
    print(f"QTWEBENGINE_CHROMIUM_FLAGS: {os.environ.get('QTWEBENGINE_CHROMIUM_FLAGS')}")
    if debug_mode:
        os.environ["QTWEBENGINE_REMOTE_DEBUGGING"] = "9222"
        print(f"QTWEBENGINE_REMOTE_DEBUGGING: {os.environ.get('QTWEBENGINE_REMOTE_DEBUGGING')}")
        
    app = QApplication.instance() or QApplication(sys.argv)
    
    # Allow Ctrl+C to kill the application gracefully
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    # Set application logo
    app.setWindowIcon(QIcon('assets/logo.jpg'))

    # Set a modern, dark theme
    app.setStyle("Fusion")
    dark_palette = app.palette()
    dark_palette.setColor(dark_palette.ColorRole.Window, QColor(45, 45, 45))
    dark_palette.setColor(dark_palette.ColorRole.WindowText, Qt.GlobalColor.white)
    dark_palette.setColor(dark_palette.ColorRole.Base, QColor(25, 25, 25))
    dark_palette.setColor(dark_palette.ColorRole.AlternateBase, QColor(53, 53, 53))
    dark_palette.setColor(dark_palette.ColorRole.ToolTipBase, QColor(25, 25, 25))
    dark_palette.setColor(dark_palette.ColorRole.ToolTipText, Qt.GlobalColor.white)
    dark_palette.setColor(dark_palette.ColorRole.Text, Qt.GlobalColor.white)
    dark_palette.setColor(dark_palette.ColorRole.Button, QColor(53, 53, 53))
    dark_palette.setColor(dark_palette.ColorRole.ButtonText, Qt.GlobalColor.white)
    dark_palette.setColor(dark_palette.ColorRole.BrightText, Qt.GlobalColor.red)
    dark_palette.setColor(dark_palette.ColorRole.Link, QColor(42, 130, 218))
    dark_palette.setColor(dark_palette.ColorRole.Highlight, QColor(42, 130, 218))
    dark_palette.setColor(dark_palette.ColorRole.HighlightedText, Qt.GlobalColor.black)
    app.setPalette(dark_palette)

    # Set a better font
    font = QFont("Cantarell", 10)
    if platform.system() == "Windows":
        font = QFont("Segoe UI", 10)
    elif platform.system() == "Darwin":
        font = QFont("San Francisco", 10)
    app.setFont(font)

    # Create a dummy parent for dialogs to ensure they are properly modal
    dummy_parent = QWidget()

    # Get profile path
    profile_path = ProfileDialog.get_profile_path(dummy_parent)
    if not profile_path:
        return 0 # Exit gracefully

    # Create a simple browser directly without Playwright for now
    # This ensures we at least get a window showing
    initial_url = None
    if len(sys.argv) > 1 and not sys.argv[1].startswith('--'):
        initial_url = sys.argv[1]

    browser = Browser(initial_url=initial_url, debug=debug_mode, profile_path=profile_path)
    browser.show()
    
    print("Browser window should be visible now")
    
    # Run the Qt event loop
    return app.exec()


# Register schemes before anything else
register_url_schemes()

if __name__ == "__main__":
    sys.exit(main())
