Skip to content

Commit 98cc07d

Browse files
committed
Implement Browser context 1
1 parent eef817f commit 98cc07d

File tree

3 files changed

+139
-87
lines changed

3 files changed

+139
-87
lines changed

src/browser/custom_browser.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,45 @@
66

77
from browser_use.browser.browser import Browser
88
from browser_use.browser.context import BrowserContext, BrowserContextConfig
9+
from playwright.async_api import BrowserContext as PlaywrightBrowserContext
10+
import logging
911

1012
from .config import BrowserPersistenceConfig
1113
from .custom_context import CustomBrowserContext
1214

15+
logger = logging.getLogger(__name__)
1316

1417
class CustomBrowser(Browser):
18+
_global_context = None
19+
1520
async def new_context(
1621
self,
1722
config: BrowserContextConfig = BrowserContextConfig(),
18-
context: CustomBrowserContext = None,
19-
) -> BrowserContext:
20-
"""Create a browser context"""
23+
context: PlaywrightBrowserContext = None,
24+
) -> 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")
2139
return CustomBrowserContext(config=config, browser=self, context=context)
40+
2241
async def close(self):
2342
"""Override close to respect persistence setting"""
24-
# Check if persistence is enabled before closing
2543
persistence_config = BrowserPersistenceConfig.from_env()
2644
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
2748
await super().close()
49+
else:
50+
logger.info("Skipping browser close due to persistent session")

src/browser/custom_context.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from browser_use.browser.browser import Browser
1313
from browser_use.browser.context import BrowserContext, BrowserContextConfig
1414
from playwright.async_api import Browser as PlaywrightBrowser
15+
from playwright.async_api import BrowserContext as PlaywrightBrowserContext
1516

1617
from .config import BrowserPersistenceConfig
1718
logger = logging.getLogger(__name__)
@@ -22,19 +23,21 @@ def __init__(
2223
self,
2324
browser: "Browser",
2425
config: BrowserContextConfig = BrowserContextConfig(),
25-
context: BrowserContext = None,
26+
context: PlaywrightBrowserContext = None,
2627
):
2728
super(CustomBrowserContext, self).__init__(browser=browser, config=config)
2829
self.context = context
29-
30+
self._persistence_config = BrowserPersistenceConfig.from_env()
3031

31-
async def _create_context(self, browser: PlaywrightBrowser):
32+
async def _create_context(self, browser: PlaywrightBrowser) -> PlaywrightBrowserContext:
3233
"""Creates a new browser context with anti-detection measures and loads cookies if available."""
3334
# If we have a context, return it directly
3435
if self.context:
3536
return self.context
36-
if self.browser.config.chrome_instance_path and len(browser.contexts) > 0:
37-
# Connect to existing Chrome instance instead of creating new one
37+
38+
# 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")
3841
context = browser.contexts[0]
3942
else:
4043
# Original code for creating new context
@@ -49,7 +52,7 @@ async def _create_context(self, browser: PlaywrightBrowser):
4952
bypass_csp=self.config.disable_security,
5053
ignore_https_errors=self.config.disable_security,
5154
record_video_dir=self.config.save_recording_path,
52-
record_video_size=self.config.browser_window_size, # set record video size, same as windows size
55+
record_video_size=self.config.browser_window_size,
5356
)
5457

5558
if self.config.trace_path:
@@ -96,3 +99,8 @@ async def _create_context(self, browser: PlaywrightBrowser):
9699
)
97100

98101
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()

webui.py

Lines changed: 98 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@
3535
from src.controller.custom_controller import CustomController
3636
from src.utils import utils
3737
from src.utils.utils import update_model_dropdown
38+
from src.browser.config import BrowserPersistenceConfig
39+
from src.browser.custom_browser import CustomBrowser
40+
from src.browser.custom_context import CustomBrowserContext
41+
from browser_use.browser.browser import BrowserConfig
42+
from browser_use.browser.context import BrowserContextConfig, BrowserContextWindowSize
43+
44+
# Global variables for persistence
45+
_global_browser = None
46+
_global_browser_context = None
47+
_global_playwright = None
3848

3949
async def run_browser_agent(
4050
agent_type,
@@ -196,96 +206,107 @@ async def run_custom_agent(
196206
max_actions_per_step,
197207
tool_call_in_content
198208
):
209+
global _global_browser, _global_browser_context, _global_playwright
210+
199211
controller = CustomController()
200-
playwright = None
201-
browser_context_ = None
212+
persistence_config = BrowserPersistenceConfig.from_env()
213+
202214
try:
203-
if use_own_browser:
204-
playwright = await async_playwright().start()
205-
chrome_exe = os.getenv("CHROME_PATH", "")
206-
chrome_use_data = os.getenv("CHROME_USER_DATA", "")
207-
208-
if chrome_exe == "":
209-
chrome_exe = None
210-
elif not os.path.exists(chrome_exe):
211-
raise ValueError(f"Chrome executable not found at {chrome_exe}")
212-
213-
if chrome_use_data == "":
214-
chrome_use_data = None
215-
216-
browser_context_ = await playwright.chromium.launch_persistent_context(
217-
user_data_dir=chrome_use_data,
218-
executable_path=chrome_exe,
219-
no_viewport=False,
220-
headless=headless, # 保持浏览器窗口可见
221-
user_agent=(
222-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
223-
"(KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
224-
),
225-
java_script_enabled=True,
226-
bypass_csp=disable_security,
227-
ignore_https_errors=disable_security,
228-
record_video_dir=save_recording_path if save_recording_path else None,
229-
record_video_size={"width": window_w, "height": window_h},
215+
# Initialize global browser if needed
216+
if _global_browser is None:
217+
_global_browser = CustomBrowser(
218+
config=BrowserConfig(
219+
headless=headless,
220+
disable_security=disable_security,
221+
extra_chromium_args=[f"--window-size={window_w},{window_h}"],
222+
)
230223
)
231-
else:
232-
browser_context_ = None
233224

234-
browser = CustomBrowser(
235-
config=BrowserConfig(
236-
headless=headless,
237-
disable_security=disable_security,
238-
extra_chromium_args=[f"--window-size={window_w},{window_h}"],
239-
)
240-
)
241-
async with await browser.new_context(
242-
config=BrowserContextConfig(
243-
trace_path=save_trace_path if save_trace_path else None,
244-
save_recording_path=save_recording_path
245-
if save_recording_path
246-
else None,
225+
# Handle browser context based on configuration
226+
if use_own_browser:
227+
if _global_browser_context is None:
228+
_global_playwright = await async_playwright().start()
229+
chrome_exe = os.getenv("CHROME_PATH", "")
230+
chrome_use_data = os.getenv("CHROME_USER_DATA", "")
231+
232+
browser_context = await _global_playwright.chromium.launch_persistent_context(
233+
user_data_dir=chrome_use_data,
234+
executable_path=chrome_exe,
247235
no_viewport=False,
248-
browser_window_size=BrowserContextWindowSize(
249-
width=window_w, height=window_h
236+
headless=headless,
237+
user_agent=(
238+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
239+
"(KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
250240
),
251-
),
252-
context=browser_context_,
253-
) as browser_context:
254-
agent = CustomAgent(
255-
task=task,
256-
add_infos=add_infos,
257-
use_vision=use_vision,
258-
llm=llm,
259-
browser_context=browser_context,
260-
controller=controller,
261-
system_prompt_class=CustomSystemPrompt,
262-
max_actions_per_step=max_actions_per_step,
263-
tool_call_in_content=tool_call_in_content
264-
)
265-
history = await agent.run(max_steps=max_steps)
241+
java_script_enabled=True,
242+
bypass_csp=disable_security,
243+
ignore_https_errors=disable_security,
244+
record_video_dir=save_recording_path if save_recording_path else None,
245+
record_video_size={"width": window_w, "height": window_h},
246+
)
247+
_global_browser_context = await _global_browser.new_context(
248+
config=BrowserContextConfig(
249+
trace_path=save_trace_path if save_trace_path else None,
250+
save_recording_path=save_recording_path if save_recording_path else None,
251+
no_viewport=False,
252+
browser_window_size=BrowserContextWindowSize(
253+
width=window_w, height=window_h
254+
),
255+
),
256+
context=browser_context,
257+
)
258+
else:
259+
if _global_browser_context is None:
260+
_global_browser_context = await _global_browser.new_context(
261+
config=BrowserContextConfig(
262+
trace_path=save_trace_path if save_trace_path else None,
263+
save_recording_path=save_recording_path if save_recording_path else None,
264+
no_viewport=False,
265+
browser_window_size=BrowserContextWindowSize(
266+
width=window_w, height=window_h
267+
),
268+
),
269+
)
266270

267-
final_result = history.final_result()
268-
errors = history.errors()
269-
model_actions = history.model_actions()
270-
model_thoughts = history.model_thoughts()
271+
# Create and run agent
272+
agent = CustomAgent(
273+
task=task,
274+
add_infos=add_infos,
275+
use_vision=use_vision,
276+
llm=llm,
277+
browser_context=_global_browser_context,
278+
controller=controller,
279+
system_prompt_class=CustomSystemPrompt,
280+
max_actions_per_step=max_actions_per_step,
281+
tool_call_in_content=tool_call_in_content
282+
)
283+
history = await agent.run(max_steps=max_steps)
284+
285+
final_result = history.final_result()
286+
errors = history.errors()
287+
model_actions = history.model_actions()
288+
model_thoughts = history.model_thoughts()
271289

272290
except Exception as e:
273291
import traceback
274-
275292
traceback.print_exc()
276-
final_result = ""
277293
errors = str(e) + "\n" + traceback.format_exc()
278-
model_actions = ""
279-
model_thoughts = ""
294+
280295
finally:
281-
# 显式关闭持久化上下文
282-
if browser_context_:
283-
await browser_context_.close()
284-
285-
# 关闭 Playwright 对象
286-
if playwright:
287-
await playwright.stop()
288-
await browser.close()
296+
# Handle cleanup based on persistence configuration
297+
if not persistence_config.persistent_session:
298+
if _global_browser_context:
299+
await _global_browser_context.close()
300+
_global_browser_context = None
301+
302+
if _global_playwright:
303+
await _global_playwright.stop()
304+
_global_playwright = None
305+
306+
if _global_browser:
307+
await _global_browser.close()
308+
_global_browser = None
309+
289310
return final_result, errors, model_actions, model_thoughts
290311

291312
# Define the theme map globally

0 commit comments

Comments
 (0)