File size: 6,329 Bytes
405ca38
 
d0404e2
 
405ca38
 
 
 
 
 
 
 
 
 
d0404e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405ca38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import asyncio
import pdb
import logging
import socket

from playwright.async_api import Browser as PlaywrightBrowser
from playwright.async_api import (
    BrowserContext as PlaywrightBrowserContext,
)
from playwright.async_api import (
    Playwright,
    async_playwright,
)

# Import browser_use components with comprehensive error handling
try:
    from browser_use.browser.browser import Browser, IN_DOCKER
    from browser_use.browser.context import BrowserContext, BrowserContextConfig
    from browser_use.browser.chrome import (
        CHROME_ARGS,
        CHROME_DETERMINISTIC_RENDERING_ARGS,
        CHROME_DISABLE_SECURITY_ARGS,
        CHROME_DOCKER_ARGS,
        CHROME_HEADLESS_ARGS,
    )
    from browser_use.browser.utils.screen_resolution import get_screen_resolution, get_window_adjustments
    from browser_use.utils import time_execution_async
except ImportError as e:
    # Fallback for different browser_use versions
    logger = logging.getLogger(__name__)
    logger.warning(f"Failed to import browser_use components: {e}")
    
    # Set default values
    IN_DOCKER = False
    
    # Try to import basic components
    try:
        from browser_use.browser.browser import Browser
        from browser_use.browser.context import BrowserContext, BrowserContextConfig
    except ImportError:
        logger.error("Could not import basic browser_use components")
        raise
    
    # Try to import chrome components with fallbacks
    try:
        from browser_use.browser.chrome import (
            CHROME_ARGS,
            CHROME_DETERMINISTIC_RENDERING_ARGS,
            CHROME_DISABLE_SECURITY_ARGS,
            CHROME_DOCKER_ARGS,
            CHROME_HEADLESS_ARGS,
        )
    except ImportError:
        logger.warning("Could not import chrome components, using defaults")
        CHROME_ARGS = []
        CHROME_DETERMINISTIC_RENDERING_ARGS = []
        CHROME_DISABLE_SECURITY_ARGS = []
        CHROME_DOCKER_ARGS = []
        CHROME_HEADLESS_ARGS = []
    
    # Try to import utility functions with fallbacks
    try:
        from browser_use.browser.utils.screen_resolution import get_screen_resolution, get_window_adjustments
    except ImportError:
        logger.warning("Could not import screen resolution utilities, using defaults")
        def get_screen_resolution():
            return {'width': 1920, 'height': 1080}
        def get_window_adjustments():
            return 0, 0
    
    try:
        from browser_use.utils import time_execution_async
    except ImportError:
        logger.warning("Could not import time_execution_async, using dummy function")
        def time_execution_async(func):
            return func

from .custom_context import CustomBrowserContext

logger = logging.getLogger(__name__)


class CustomBrowser(Browser):

    async def new_context(self, config: BrowserContextConfig | None = None) -> CustomBrowserContext:
        """Create a browser context"""
        browser_config = self.config.model_dump() if self.config else {}
        context_config = config.model_dump() if config else {}
        merged_config = {**browser_config, **context_config}
        return CustomBrowserContext(config=BrowserContextConfig(**merged_config), browser=self)

    async def _setup_builtin_browser(self, playwright: Playwright) -> PlaywrightBrowser:
        """Sets up and returns a Playwright Browser instance with anti-detection measures."""
        assert self.config.browser_binary_path is None, 'browser_binary_path should be None if trying to use the builtin browsers'

        # Use the configured window size from new_context_config if available
        if (
                not self.config.headless
                and hasattr(self.config, 'new_context_config')
                and hasattr(self.config.new_context_config, 'window_width')
                and hasattr(self.config.new_context_config, 'window_height')
        ):
            screen_size = {
                'width': self.config.new_context_config.window_width,
                'height': self.config.new_context_config.window_height,
            }
            offset_x, offset_y = get_window_adjustments()
        elif self.config.headless:
            screen_size = {'width': 1920, 'height': 1080}
            offset_x, offset_y = 0, 0
        else:
            screen_size = get_screen_resolution()
            offset_x, offset_y = get_window_adjustments()

        chrome_args = {
            f'--remote-debugging-port={self.config.chrome_remote_debugging_port}',
            *CHROME_ARGS,
            *(CHROME_DOCKER_ARGS if IN_DOCKER else []),
            *(CHROME_HEADLESS_ARGS if self.config.headless else []),
            *(CHROME_DISABLE_SECURITY_ARGS if self.config.disable_security else []),
            *(CHROME_DETERMINISTIC_RENDERING_ARGS if self.config.deterministic_rendering else []),
            f'--window-position={offset_x},{offset_y}',
            f'--window-size={screen_size["width"]},{screen_size["height"]}',
            *self.config.extra_browser_args,
        }

        # check if chrome remote debugging port is already taken,
        # if so remove the remote-debugging-port arg to prevent conflicts
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            if s.connect_ex(('localhost', self.config.chrome_remote_debugging_port)) == 0:
                chrome_args.remove(f'--remote-debugging-port={self.config.chrome_remote_debugging_port}')

        browser_class = getattr(playwright, self.config.browser_class)
        args = {
            'chromium': list(chrome_args),
            'firefox': [
                *{
                    '-no-remote',
                    *self.config.extra_browser_args,
                }
            ],
            'webkit': [
                *{
                    '--no-startup-window',
                    *self.config.extra_browser_args,
                }
            ],
        }

        browser = await browser_class.launch(
            channel='chromium',  # https://github.com/microsoft/playwright/issues/33566
            headless=self.config.headless,
            args=args[self.config.browser_class],
            proxy=self.config.proxy.model_dump() if self.config.proxy else None,
            handle_sigterm=False,
            handle_sigint=False,
        )
        return browser