Skip to content

Commit db73db1

Browse files
committed
fix macos cannot use own browser
1 parent b00ebf5 commit db73db1

File tree

4 files changed

+338
-122
lines changed

4 files changed

+338
-122
lines changed

src/browser/custom_browser.py

Lines changed: 104 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
# @ProjectName: browser-use-webui
55
# @FileName: browser.py
66

7+
import asyncio
8+
9+
from playwright.async_api import Browser as PlaywrightBrowser
10+
from playwright.async_api import (
11+
BrowserContext as PlaywrightBrowserContext,
12+
)
13+
from playwright.async_api import (
14+
Playwright,
15+
async_playwright,
16+
)
717
from browser_use.browser.browser import Browser
818
from browser_use.browser.context import BrowserContext, BrowserContextConfig
919
from playwright.async_api import BrowserContext as PlaywrightBrowserContext
@@ -15,36 +25,102 @@
1525
logger = logging.getLogger(__name__)
1626

1727
class CustomBrowser(Browser):
18-
_global_context = None
1928

2029
async def new_context(
2130
self,
22-
config: BrowserContextConfig = BrowserContextConfig(),
23-
context: PlaywrightBrowserContext = None,
31+
config: BrowserContextConfig = BrowserContextConfig()
2432
) -> CustomBrowserContext:
25-
"""Create a browser context with persistence support"""
26-
persistence_config = BrowserPersistenceConfig.from_env()
27-
28-
if persistence_config.persistent_session:
29-
if CustomBrowser._global_context is not None:
30-
logger.info("Reusing existing persistent browser context")
31-
return CustomBrowser._global_context
32-
33-
context_instance = CustomBrowserContext(config=config, browser=self, context=context)
34-
CustomBrowser._global_context = context_instance
35-
logger.info("Created new persistent browser context")
36-
return context_instance
37-
38-
logger.info("Creating non-persistent browser context")
39-
return CustomBrowserContext(config=config, browser=self, context=context)
40-
41-
async def close(self):
42-
"""Override close to respect persistence setting"""
43-
persistence_config = BrowserPersistenceConfig.from_env()
44-
if not persistence_config.persistent_session:
45-
if CustomBrowser._global_context is not None:
46-
await CustomBrowser._global_context.close()
47-
CustomBrowser._global_context = None
48-
await super().close()
33+
return CustomBrowserContext(config=config, browser=self)
34+
35+
async def _setup_browser(self, playwright: Playwright) -> PlaywrightBrowser:
36+
"""Sets up and returns a Playwright Browser instance with anti-detection measures."""
37+
if self.config.wss_url:
38+
browser = await playwright.chromium.connect(self.config.wss_url)
39+
return browser
40+
elif self.config.chrome_instance_path:
41+
import subprocess
42+
43+
import requests
44+
45+
try:
46+
# Check if browser is already running
47+
response = requests.get('http://localhost:9222/json/version', timeout=2)
48+
if response.status_code == 200:
49+
logger.info('Reusing existing Chrome instance')
50+
browser = await playwright.chromium.connect_over_cdp(
51+
endpoint_url='http://localhost:9222',
52+
timeout=20000, # 20 second timeout for connection
53+
)
54+
return browser
55+
except requests.ConnectionError:
56+
logger.debug('No existing Chrome instance found, starting a new one')
57+
58+
# Start a new Chrome instance
59+
subprocess.Popen(
60+
[
61+
self.config.chrome_instance_path,
62+
'--remote-debugging-port=9222',
63+
],
64+
stdout=subprocess.DEVNULL,
65+
stderr=subprocess.DEVNULL,
66+
)
67+
68+
# Attempt to connect again after starting a new instance
69+
for _ in range(10):
70+
try:
71+
response = requests.get('http://localhost:9222/json/version', timeout=2)
72+
if response.status_code == 200:
73+
break
74+
except requests.ConnectionError:
75+
pass
76+
await asyncio.sleep(1)
77+
78+
try:
79+
browser = await playwright.chromium.connect_over_cdp(
80+
endpoint_url='http://localhost:9222',
81+
timeout=20000, # 20 second timeout for connection
82+
)
83+
return browser
84+
except Exception as e:
85+
logger.error(f'Failed to start a new Chrome instance.: {str(e)}')
86+
raise RuntimeError(
87+
' To start chrome in Debug mode, you need to close all existing Chrome instances and try again otherwise we can not connect to the instance.'
88+
)
89+
4990
else:
50-
logger.info("Skipping browser close due to persistent session")
91+
try:
92+
disable_security_args = []
93+
if self.config.disable_security:
94+
disable_security_args = [
95+
'--disable-web-security',
96+
'--disable-site-isolation-trials',
97+
'--disable-features=IsolateOrigins,site-per-process',
98+
]
99+
100+
browser = await playwright.chromium.launch(
101+
headless=self.config.headless,
102+
args=[
103+
'--no-sandbox',
104+
'--disable-blink-features=AutomationControlled',
105+
'--disable-infobars',
106+
'--disable-background-timer-throttling',
107+
'--disable-popup-blocking',
108+
'--disable-backgrounding-occluded-windows',
109+
'--disable-renderer-backgrounding',
110+
'--disable-window-activation',
111+
'--disable-focus-on-load',
112+
'--no-first-run',
113+
'--no-default-browser-check',
114+
'--no-startup-window',
115+
'--window-position=0,0',
116+
# '--window-size=1280,1000',
117+
]
118+
+ disable_security_args
119+
+ self.config.extra_chromium_args,
120+
proxy=self.config.proxy,
121+
)
122+
123+
return browser
124+
except Exception as e:
125+
logger.error(f'Failed to initialize Playwright browser: {str(e)}')
126+
raise

src/browser/custom_context.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,17 @@ class CustomBrowserContext(BrowserContext):
2222
def __init__(
2323
self,
2424
browser: "Browser",
25-
config: BrowserContextConfig = BrowserContextConfig(),
26-
context: PlaywrightBrowserContext = None,
25+
config: BrowserContextConfig = BrowserContextConfig()
2726
):
2827
super(CustomBrowserContext, self).__init__(browser=browser, config=config)
29-
self.context = context
30-
self._persistence_config = BrowserPersistenceConfig.from_env()
3128

3229
async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowserContext:
3330
"""Creates a new browser context with anti-detection measures and loads cookies if available."""
3431
# If we have a context, return it directly
35-
if self.context:
36-
return self.context
3732

3833
# Check if we should use existing context for persistence
39-
if self._persistence_config.persistent_session and len(browser.contexts) > 0:
40-
logger.info("Using existing persistent context")
34+
if self.browser.config.chrome_instance_path and len(browser.contexts) > 0:
35+
# Connect to existing Chrome instance instead of creating new one
4136
context = browser.contexts[0]
4237
else:
4338
# Original code for creating new context
@@ -99,8 +94,3 @@ async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowser
9994
)
10095

10196
return context
102-
103-
async def close(self):
104-
"""Override close to respect persistence setting"""
105-
if not self._persistence_config.persistent_session:
106-
await super().close()

tests/test_browser_use.py

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# @Author : wenshao
44
# @ProjectName: browser-use-webui
55
# @FileName: test_browser_use.py
6+
import pdb
67

78
from dotenv import load_dotenv
89

@@ -28,20 +29,29 @@ async def test_browser_use_org():
2829
BrowserContextWindowSize,
2930
)
3031

32+
# llm = utils.get_llm_model(
33+
# provider="azure_openai",
34+
# model_name="gpt-4o",
35+
# temperature=0.8,
36+
# base_url=os.getenv("AZURE_OPENAI_ENDPOINT", ""),
37+
# api_key=os.getenv("AZURE_OPENAI_API_KEY", ""),
38+
# )
39+
3140
llm = utils.get_llm_model(
32-
provider="azure_openai",
33-
model_name="gpt-4o",
34-
temperature=0.8,
35-
base_url=os.getenv("AZURE_OPENAI_ENDPOINT", ""),
36-
api_key=os.getenv("AZURE_OPENAI_API_KEY", ""),
41+
provider="deepseek",
42+
model_name="deepseek-chat",
43+
temperature=0.8
3744
)
3845

3946
window_w, window_h = 1920, 1080
47+
use_vision = False
48+
chrome_path = os.getenv("CHROME_PATH", None)
4049

4150
browser = Browser(
4251
config=BrowserConfig(
4352
headless=False,
4453
disable_security=True,
54+
chrome_instance_path=chrome_path,
4555
extra_chromium_args=[f"--window-size={window_w},{window_h}"],
4656
)
4757
)
@@ -59,6 +69,7 @@ async def test_browser_use_org():
5969
task="go to google.com and type 'OpenAI' click search and give me the first url",
6070
llm=llm,
6171
browser_context=browser_context,
72+
use_vision=use_vision
6273
)
6374
history: AgentHistoryList = await agent.run(max_steps=10)
6475

@@ -208,6 +219,122 @@ async def test_browser_use_custom():
208219
await browser.close()
209220

210221

222+
async def test_browser_use_custom_v2():
223+
from browser_use.browser.context import BrowserContextWindowSize
224+
from browser_use.browser.browser import BrowserConfig
225+
from playwright.async_api import async_playwright
226+
227+
from src.agent.custom_agent import CustomAgent
228+
from src.agent.custom_prompts import CustomSystemPrompt
229+
from src.browser.custom_browser import CustomBrowser
230+
from src.browser.custom_context import BrowserContextConfig
231+
from src.controller.custom_controller import CustomController
232+
233+
window_w, window_h = 1920, 1080
234+
235+
# llm = utils.get_llm_model(
236+
# provider="azure_openai",
237+
# model_name="gpt-4o",
238+
# temperature=0.8,
239+
# base_url=os.getenv("AZURE_OPENAI_ENDPOINT", ""),
240+
# api_key=os.getenv("AZURE_OPENAI_API_KEY", ""),
241+
# )
242+
243+
# llm = utils.get_llm_model(
244+
# provider="gemini",
245+
# model_name="gemini-2.0-flash-exp",
246+
# temperature=1.0,
247+
# api_key=os.getenv("GOOGLE_API_KEY", "")
248+
# )
249+
250+
llm = utils.get_llm_model(
251+
provider="deepseek",
252+
model_name="deepseek-chat",
253+
temperature=0.8
254+
)
255+
256+
# llm = utils.get_llm_model(
257+
# provider="ollama", model_name="qwen2.5:7b", temperature=0.8
258+
# )
259+
260+
controller = CustomController()
261+
use_own_browser = True
262+
disable_security = True
263+
use_vision = False # Set to False when using DeepSeek
264+
tool_call_in_content = True # Set to True when using Ollama
265+
max_actions_per_step = 1
266+
playwright = None
267+
browser = None
268+
browser_context = None
269+
270+
try:
271+
if use_own_browser:
272+
chrome_path = os.getenv("CHROME_PATH", None)
273+
if chrome_path == "":
274+
chrome_path = None
275+
else:
276+
chrome_path = None
277+
browser = CustomBrowser(
278+
config=BrowserConfig(
279+
headless=False,
280+
disable_security=disable_security,
281+
chrome_instance_path=chrome_path,
282+
extra_chromium_args=[f"--window-size={window_w},{window_h}"],
283+
)
284+
)
285+
browser_context = await browser.new_context(
286+
config=BrowserContextConfig(
287+
trace_path="./tmp/traces",
288+
save_recording_path="./tmp/record_videos",
289+
no_viewport=False,
290+
browser_window_size=BrowserContextWindowSize(
291+
width=window_w, height=window_h
292+
),
293+
)
294+
)
295+
agent = CustomAgent(
296+
task="go to google.com and type 'OpenAI' click search and give me the first url",
297+
add_infos="", # some hints for llm to complete the task
298+
llm=llm,
299+
browser=browser,
300+
browser_context=browser_context,
301+
controller=controller,
302+
system_prompt_class=CustomSystemPrompt,
303+
use_vision=use_vision,
304+
tool_call_in_content=tool_call_in_content,
305+
max_actions_per_step=max_actions_per_step
306+
)
307+
history: AgentHistoryList = await agent.run(max_steps=10)
308+
309+
print("Final Result:")
310+
pprint(history.final_result(), indent=4)
311+
312+
print("\nErrors:")
313+
pprint(history.errors(), indent=4)
314+
315+
# e.g. xPaths the model clicked on
316+
print("\nModel Outputs:")
317+
pprint(history.model_actions(), indent=4)
318+
319+
print("\nThoughts:")
320+
pprint(history.model_thoughts(), indent=4)
321+
# close browser
322+
except Exception:
323+
import traceback
324+
325+
traceback.print_exc()
326+
finally:
327+
# 显式关闭持久化上下文
328+
if browser_context:
329+
await browser_context.close()
330+
331+
# 关闭 Playwright 对象
332+
if playwright:
333+
await playwright.stop()
334+
if browser:
335+
await browser.close()
336+
211337
if __name__ == "__main__":
212338
# asyncio.run(test_browser_use_org())
213-
asyncio.run(test_browser_use_custom())
339+
# asyncio.run(test_browser_use_custom())
340+
asyncio.run(test_browser_use_custom_v2())

0 commit comments

Comments
 (0)