diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md index 0065d6b4..27f8ac81 100644 --- a/examples/cdp_mode/ReadMe.md +++ b/examples/cdp_mode/ReadMe.md @@ -420,6 +420,13 @@ sb.cdp.minimize() sb.cdp.medimize() sb.cdp.set_window_rect() sb.cdp.reset_window_size() +sb.cdp.switch_to_window(window) +sb.cdp.switch_to_newest_window() +sb.cdp.switch_to_tab(tab) +sb.cdp.switch_to_newest_tab() +sb.cdp.close_active_tab() +sb.cdp.get_active_tab() +sb.cdp.get_tabs() sb.cdp.get_window() sb.cdp.get_text(selector) sb.cdp.get_title() diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 8d6e9956..30ef2bf1 100644 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -682,6 +682,13 @@ def uc_open_with_cdp_mode(driver, url=None): cdp.gui_hover_element = CDPM.gui_hover_element cdp.gui_hover_and_click = CDPM.gui_hover_and_click cdp.internalize_links = CDPM.internalize_links + cdp.switch_to_window = CDPM.switch_to_window + cdp.switch_to_newest_window = CDPM.switch_to_newest_window + cdp.switch_to_tab = CDPM.switch_to_tab + cdp.switch_to_newest_tab = CDPM.switch_to_newest_tab + cdp.close_active_tab = CDPM.close_active_tab + cdp.get_active_tab = CDPM.get_active_tab + cdp.get_tabs = CDPM.get_tabs cdp.get_window = CDPM.get_window cdp.get_element_attributes = CDPM.get_element_attributes cdp.get_element_attribute = CDPM.get_element_attribute @@ -2033,6 +2040,7 @@ def _set_chrome_options( prefs["download.default_directory"] = downloads_path prefs["download.directory_upgrade"] = True prefs["download.prompt_for_download"] = False + prefs["download_bubble.partial_view_enabled"] = False prefs["credentials_enable_service"] = False prefs["local_discovery.notifications_enabled"] = False prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py index e0b6c634..a4f5fba4 100644 --- a/seleniumbase/core/sb_cdp.py +++ b/seleniumbase/core/sb_cdp.py @@ -1014,10 +1014,51 @@ class CDPMethods(): self.set_window_rect(x, y, width, height) self.__add_light_pause() + def switch_to_window(self, window): + self.switch_to_tab(window) + + def switch_to_newest_window(self): + self.switch_to_tab(-1) + + def switch_to_tab(self, tab): + driver = self.driver + if hasattr(driver, "cdp_base"): + driver = driver.cdp_base + if isinstance(tab, int): + self.page = driver.tabs[tab] + elif isinstance(tab, cdp_util.Tab): + self.page = tab + else: + raise Exception("`tab` must be an int or a Tab type!") + self.bring_active_window_to_front() + + def switch_to_newest_tab(self): + self.switch_to_tab(-1) + + def close_active_tab(self): + """Close the active tab. + The active tab is the one currenly controlled by CDP. + The active tab MIGHT NOT be the currently visible tab! + (If a page opens a new tab, the new tab WON'T be active) + To switch the active tab, call: sb.switch_to_tab(tab)""" + return self.loop.run_until_complete(self.page.close()) + + def get_active_tab(self): + """Return the active tab. + The active tab is the one currenly controlled by CDP. + The active tab MIGHT NOT be the currently visible tab! + (If a page opens a new tab, the new tab WON'T be active) + To switch the active tab, call: sb.switch_to_tab(tab)""" + return self.page + + def get_tabs(self): + driver = self.driver + if hasattr(driver, "cdp_base"): + driver = driver.cdp_base + return driver.tabs + def get_window(self): - return self.loop.run_until_complete( - self.page.get_window() - ) + return self.loop.run_until_complete(self.page.get_window()) def get_text(self, selector): return self.find_element(selector).text_all @@ -1211,14 +1252,10 @@ class CDPMethods(): return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5) def get_document(self): - return self.loop.run_until_complete( - self.page.get_document() - ) + return self.loop.run_until_complete(self.page.get_document()) def get_flattened_document(self): - return self.loop.run_until_complete( - self.page.get_flattened_document() - ) + return self.loop.run_until_complete(self.page.get_flattened_document()) def get_element_attributes(self, selector): selector = self.__convert_to_css_if_xpath(selector) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 5a1dd8c5..036dd305 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -3918,6 +3918,9 @@ class BaseCase(unittest.TestCase): timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) + if self.__is_cdp_swap_needed() and not isinstance(window, str): + self.cdp.switch_to_tab(window) + return page_actions.switch_to_window(self.driver, window, timeout) def switch_to_default_window(self): diff --git a/seleniumbase/undetected/cdp_driver/browser.py b/seleniumbase/undetected/cdp_driver/browser.py index 638ef8e0..2c0051e4 100644 --- a/seleniumbase/undetected/cdp_driver/browser.py +++ b/seleniumbase/undetected/cdp_driver/browser.py @@ -660,7 +660,7 @@ class CookieJar: break else: connection = self._browser.connection - cookies = await connection.send(cdp.storage.get_cookies()) + cookies = await connection.send(cdp.network.get_cookies()) if requests_cookie_format: import requests.cookies @@ -690,8 +690,7 @@ class CookieJar: break else: connection = self._browser.connection - cookies = await connection.send(cdp.storage.get_cookies()) - await connection.send(cdp.storage.set_cookies(cookies)) + await connection.send(cdp.network.set_cookies(cookies)) async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"): """ @@ -718,7 +717,7 @@ class CookieJar: break else: connection = self._browser.connection - cookies = await connection.send(cdp.storage.get_cookies()) + cookies = await connection.send(cdp.network.get_cookies()) # if not connection: # return # if not connection.websocket: @@ -776,7 +775,7 @@ class CookieJar: cookie.value, ) break - await connection.send(cdp.storage.set_cookies(included_cookies)) + await connection.send(cdp.network.set_cookies(included_cookies)) async def clear(self): """ @@ -791,9 +790,9 @@ class CookieJar: break else: connection = self._browser.connection - cookies = await connection.send(cdp.storage.get_cookies()) + cookies = await connection.send(cdp.network.get_cookies()) if cookies: - await connection.send(cdp.storage.clear_cookies()) + await connection.send(cdp.network.clear_cookies()) class HTTPApi: diff --git a/seleniumbase/undetected/cdp_driver/connection.py b/seleniumbase/undetected/cdp_driver/connection.py index da9df3da..83643feb 100644 --- a/seleniumbase/undetected/cdp_driver/connection.py +++ b/seleniumbase/undetected/cdp_driver/connection.py @@ -382,7 +382,7 @@ class Connection(metaclass=CantTouchThis): async def send( self, cdp_obj: Generator[dict[str, Any], dict[str, Any], Any], - _is_update=False, + _is_update=True, ) -> Any: """ Send a protocol command. diff --git a/seleniumbase/undetected/cdp_driver/tab.py b/seleniumbase/undetected/cdp_driver/tab.py index 3f743549..3154ff20 100644 --- a/seleniumbase/undetected/cdp_driver/tab.py +++ b/seleniumbase/undetected/cdp_driver/tab.py @@ -852,6 +852,7 @@ class Tab(Connection): await self.send( cdp.target.close_target(target_id=self.target.target_id) ) + await asyncio.sleep(0.1) async def get_window(self) -> Tuple[ cdp.browser.WindowID, cdp.browser.Bounds