From e86ac7517d853d9745cd54c7dda721e3ea4ab931 Mon Sep 17 00:00:00 2001
From: g1879 <g1879@qq.com>
Date: Mon, 25 Dec 2023 18:00:13 +0800
Subject: [PATCH] =?UTF-8?q?4.0.0b29(+)=20=E5=A2=9E=E5=8A=A0disconnect()?=
 =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=9B=20=E4=BC=98=E5=8C=96=E6=9F=A5?=
 =?UTF-8?q?=E6=89=BE=E5=85=83=E7=B4=A0=E9=80=BB=E8=BE=91=EF=BC=9B=20?=
 =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=A8=E4=BD=9C=E9=93=BE=E6=8C=89=E9=94=AE?=
 =?UTF-8?q?=E8=B6=85=E6=97=B6=E9=97=AE=E9=A2=98=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 DrissionPage/__init__.py                    |   2 +-
 DrissionPage/_base/base.py                  |  22 +-
 DrissionPage/_base/browser.py               |   2 +-
 DrissionPage/_base/driver.py                |  24 +-
 DrissionPage/_elements/chromium_element.py  | 270 +++++++++++---------
 DrissionPage/_elements/chromium_element.pyi |  16 +-
 DrissionPage/_functions/browser.py          |   2 +-
 DrissionPage/_functions/keys.py             |   2 +-
 DrissionPage/_functions/tools.py            |   4 +-
 DrissionPage/_pages/chromium_base.py        | 100 ++++----
 DrissionPage/_pages/chromium_base.pyi       |   2 +
 DrissionPage/_pages/chromium_frame.py       |  23 +-
 DrissionPage/_pages/chromium_frame.pyi      |   3 +-
 DrissionPage/_pages/chromium_page.py        |   4 +-
 DrissionPage/_pages/chromium_tab.py         |   8 +-
 DrissionPage/_pages/session_page.py         |   2 +-
 DrissionPage/_pages/web_page.py             |   8 +-
 DrissionPage/_units/actions.py              |   2 -
 DrissionPage/_units/clicker.py              |   4 +-
 19 files changed, 263 insertions(+), 237 deletions(-)

diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py
index 5ee4cc9..098d123 100644
--- a/DrissionPage/__init__.py
+++ b/DrissionPage/__init__.py
@@ -13,4 +13,4 @@ from ._configs.chromium_options import ChromiumOptions
 from ._configs.session_options import SessionOptions
 
 __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__']
-__version__ = '4.0.0b28'
+__version__ = '4.0.0b29'
diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py
index 8549d78..8f2598d 100644
--- a/DrissionPage/_base/base.py
+++ b/DrissionPage/_base/base.py
@@ -144,7 +144,7 @@ class DrissionElement(BaseElement):
         """返回直接子元素元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
         :param index: 第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 直接子元素或节点文本组成的列表
         """
@@ -174,7 +174,7 @@ class DrissionElement(BaseElement):
         """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素
         """
@@ -194,7 +194,7 @@ class DrissionElement(BaseElement):
         """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 后面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素
         """
@@ -214,7 +214,7 @@ class DrissionElement(BaseElement):
         """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素前面的某个元素或节点
         """
@@ -234,7 +234,7 @@ class DrissionElement(BaseElement):
         """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 后面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素后面的某个元素或节点
         """
@@ -253,7 +253,7 @@ class DrissionElement(BaseElement):
     def children(self, filter_loc='', timeout=None, ele_only=True):
         """返回直接子元素元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 直接子元素或节点文本组成的列表
         """
@@ -272,7 +272,7 @@ class DrissionElement(BaseElement):
     def prevs(self, filter_loc='', timeout=None, ele_only=True):
         """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本组成的列表
         """
@@ -281,7 +281,7 @@ class DrissionElement(BaseElement):
     def nexts(self, filter_loc='', timeout=None, ele_only=True):
         """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本组成的列表
         """
@@ -290,7 +290,7 @@ class DrissionElement(BaseElement):
     def befores(self, filter_loc='', timeout=None, ele_only=True):
         """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素前面的元素或节点组成的列表
         """
@@ -300,7 +300,7 @@ class DrissionElement(BaseElement):
     def afters(self, filter_loc='', timeout=None, ele_only=True):
         """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素后面的元素或节点组成的列表
         """
@@ -314,7 +314,7 @@ class DrissionElement(BaseElement):
         :param filter_loc: 用于筛选的查询语法
         :param direction: 'following' 或 'preceding',查找的方向
         :param brother: 查找范围,在同级查找还是整个dom前后查找
-        :param timeout: 查找等待时间
+        :param timeout: 查找等待时间(秒)
         :return: 元素对象或字符串
         """
         if index is not None and index < 1:
diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py
index 881b794..2d8b891 100644
--- a/DrissionPage/_base/browser.py
+++ b/DrissionPage/_base/browser.py
@@ -160,7 +160,7 @@ class Browser(object):
 
     def quit(self, timeout=5, force=False):
         """关闭浏览器
-        :param timeout: 等待浏览器关闭超时时间
+        :param timeout: 等待浏览器关闭超时时间(秒)
         :param force: 是否立刻强制终止进程
         :return: None
         """
diff --git a/DrissionPage/_base/driver.py b/DrissionPage/_base/driver.py
index caf2404..46a52fb 100644
--- a/DrissionPage/_base/driver.py
+++ b/DrissionPage/_base/driver.py
@@ -3,13 +3,14 @@
 @Author  :   g1879
 @Contact :   g1879@qq.com
 """
-from json import dumps, loads
+from json import dumps, loads, JSONDecodeError
 from queue import Queue, Empty
 from threading import Thread, Event
 from time import perf_counter
 
 from requests import get
-from websocket import WebSocketTimeoutException, WebSocketConnectionClosedException, create_connection
+from websocket import (WebSocketTimeoutException, WebSocketConnectionClosedException, create_connection,
+                       WebSocketException)
 
 
 class Driver(object):
@@ -74,7 +75,7 @@ class Driver(object):
 
         except (OSError, WebSocketConnectionClosedException):
             self.method_results.pop(ws_id, None)
-            return None
+            return {'error': {'message': 'page closed'}}
 
         while not self._stopped.is_set():
             try:
@@ -102,7 +103,7 @@ class Driver(object):
                 msg = loads(msg_json)
             except WebSocketTimeoutException:
                 continue
-            except:
+            except (WebSocketException, OSError, WebSocketConnectionClosedException, JSONDecodeError):
                 self.stop()
                 return
 
@@ -119,10 +120,10 @@ class Driver(object):
             if 'method' in msg:
                 if msg['method'].startswith('Page.javascriptDialog'):
                     self.alert_flag = msg['method'].endswith('Opening')
-                if msg['method'] in self.immediate_event_handlers:
-                    function = self.immediate_event_handlers.get(msg['method'])
-                    if function:
-                        function(**msg['params'])
+                function = self.immediate_event_handlers.get(msg['method'])
+                if function:
+                    Thread(target=function, kwargs=msg['params']).start()
+                    # function(**msg['params'])
                 else:
                     self.event_queue.put(msg)
 
@@ -154,12 +155,10 @@ class Driver(object):
         :return: 执行结果
         """
         if self._stopped.is_set():
-            return {'error': 'tab closed', 'type': 'tab_closed'}
+            return {'error': 'page closed', 'type': 'page_closed'}
 
         timeout = kwargs.pop('_timeout', 15)
         result = self._send({'method': _method, 'params': kwargs}, timeout=timeout)
-        if result is None:
-            return {'error': 'tab closed', 'type': 'tab_closed'}
         if 'result' not in result and 'error' in result:
             return {'error': result['error']['message'], 'type': result.get('type', 'call_method_error'),
                     'method': _method, 'args': kwargs}
@@ -169,8 +168,7 @@ class Driver(object):
     def start(self):
         """启动连接"""
         self._stopped.clear()
-        self._ws = create_connection(self._websocket_url, enable_multithread=True,
-                                     suppress_origin=True)
+        self._ws = create_connection(self._websocket_url, enable_multithread=True, suppress_origin=True)
         self._recv_th.start()
         self._handle_event_th.start()
         return True
diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py
index 63f0ff0..ce88ff5 100644
--- a/DrissionPage/_elements/chromium_element.py
+++ b/DrissionPage/_elements/chromium_element.py
@@ -80,7 +80,7 @@ class ChromiumElement(DrissionElement):
     def __call__(self, loc_or_str, timeout=None):
         """在内部查找元素
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: ChromiumElement对象或属性、文本
         """
         return self.ele(loc_or_str, timeout)
@@ -214,31 +214,31 @@ class ChromiumElement(DrissionElement):
         """
         return super().parent(level_or_loc, index)
 
-    def child(self, filter_loc='', index=1, timeout=0, ele_only=True):
+    def child(self, filter_loc='', index=1, timeout=None, ele_only=True):
         """返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 直接子元素或节点文本
         """
         return super().child(filter_loc, index, timeout, ele_only=ele_only)
 
-    def prev(self, filter_loc='', index=1, timeout=0, ele_only=True):
+    def prev(self, filter_loc='', index=1, timeout=None, ele_only=True):
         """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本
         """
         return super().prev(filter_loc, index, timeout, ele_only=ele_only)
 
-    def next(self, filter_loc='', index=1, timeout=0, ele_only=True):
+    def next(self, filter_loc='', index=1, timeout=None, ele_only=True):
         """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本
         """
@@ -249,7 +249,7 @@ class ChromiumElement(DrissionElement):
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素前面的某个元素或节点
         """
@@ -260,34 +260,34 @@ class ChromiumElement(DrissionElement):
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
         :param index: 第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素后面的某个元素或节点
         """
         return super().after(filter_loc, index, timeout, ele_only=ele_only)
 
-    def children(self, filter_loc='', timeout=0, ele_only=True):
+    def children(self, filter_loc='', timeout=None, ele_only=True):
         """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 直接子元素或节点文本组成的列表
         """
         return super().children(filter_loc, timeout, ele_only=ele_only)
 
-    def prevs(self, filter_loc='', timeout=0, ele_only=True):
+    def prevs(self, filter_loc='', timeout=None, ele_only=True):
         """返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本组成的列表
         """
         return super().prevs(filter_loc, timeout, ele_only=ele_only)
 
-    def nexts(self, filter_loc='', timeout=0, ele_only=True):
+    def nexts(self, filter_loc='', timeout=None, ele_only=True):
         """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 兄弟元素或节点文本组成的列表
         """
@@ -297,7 +297,7 @@ class ChromiumElement(DrissionElement):
         """返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素前面的元素或节点组成的列表
         """
@@ -307,7 +307,7 @@ class ChromiumElement(DrissionElement):
         """返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素后面的元素或节点组成的列表
         """
@@ -367,7 +367,7 @@ class ChromiumElement(DrissionElement):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: 运行的结果
         """
         return run_js(self, script, as_expr, self.page.timeouts.script if timeout is None else timeout, args)
@@ -377,7 +377,7 @@ class ChromiumElement(DrissionElement):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: None
         """
         from threading import Thread
@@ -387,7 +387,7 @@ class ChromiumElement(DrissionElement):
     def ele(self, loc_or_str, timeout=None):
         """返回当前元素下级符合条件的第一个元素、属性或节点文本
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致
         :return: ChromiumElement对象或属性、文本
         """
         return self._ele(loc_or_str, timeout, method='ele()')
@@ -395,7 +395,7 @@ class ChromiumElement(DrissionElement):
     def eles(self, loc_or_str, timeout=None):
         """返回当前元素下级所有符合条件的子元素、属性或节点文本
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致
         :return: ChromiumElement对象或属性、文本组成的列表
         """
         return self._ele(loc_or_str, timeout=timeout, single=False)
@@ -429,7 +429,7 @@ class ChromiumElement(DrissionElement):
     def _find_elements(self, loc_or_str, timeout=None, single=True, relative=False, raise_err=None):
         """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间
+        :param timeout: 查找元素超时时间(秒)
         :param single: True则返回第一个,False则返回全部
         :param relative: WebPage用的表示是否相对定位的参数
         :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置
@@ -449,7 +449,7 @@ class ChromiumElement(DrissionElement):
 
     def get_src(self, timeout=None, base64_to_bytes=True):
         """返回元素src资源,base64的可转为bytes返回,其它返回str
-        :param timeout: 等待资源加载的超时时间
+        :param timeout: 等待资源加载的超时时间(秒)
         :param base64_to_bytes: 为True时,如果是base64数据,转换为bytes格式
         :return: 资源内容
         """
@@ -532,7 +532,7 @@ class ChromiumElement(DrissionElement):
         """保存图片或其它有src属性的元素的资源
         :param path: 文件保存路径,为None时保存到当前文件夹
         :param name: 文件名称,为None时从资源url获取
-        :param timeout: 等待资源加载的超时时间
+        :param timeout: 等待资源加载的超时时间(秒)
         :return: 返回保存路径
         """
         data = self.get_src(timeout=timeout)
@@ -603,7 +603,6 @@ class ChromiumElement(DrissionElement):
         if clear and vals not in ('\n', '\ue007'):
             self.clear(by_js=False)
         else:
-            self.wait.has_rect()
             self._input_focus()
 
         input_text_or_keys(self.page, vals)
@@ -793,7 +792,7 @@ class ShadowRoot(BaseElement):
         """在内部查找元素
         例:ele2 = ele1('@id=ele_id')
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 元素对象或属性、文本
         """
         return self.ele(loc_or_str, timeout)
@@ -825,7 +824,7 @@ class ShadowRoot(BaseElement):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: 运行的结果
         """
         return run_js(self, script, as_expr, self.page.timeouts.script if timeout is None else timeout, args)
@@ -835,7 +834,7 @@ class ShadowRoot(BaseElement):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: None
         """
         from threading import Thread
@@ -986,7 +985,7 @@ class ShadowRoot(BaseElement):
     def ele(self, loc_or_str, timeout=None):
         """返回当前元素下级符合条件的第一个元素
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致
         :return: ChromiumElement对象
         """
         return self._ele(loc_or_str, timeout, method='ele()')
@@ -994,7 +993,7 @@ class ShadowRoot(BaseElement):
     def eles(self, loc_or_str, timeout=None):
         """返回当前元素下级所有符合条件的子元素
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致
         :return: ChromiumElement对象组成的列表
         """
         return self._ele(loc_or_str, timeout=timeout, single=False)
@@ -1020,7 +1019,7 @@ class ShadowRoot(BaseElement):
     def _find_elements(self, loc_or_str, timeout=None, single=True, relative=False, raise_err=None):
         """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间
+        :param timeout: 查找元素超时时间(秒)
         :param single: True则返回第一个,False则返回全部
         :param relative: WebPage用的表示是否相对定位的参数
         :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置
@@ -1030,42 +1029,47 @@ class ShadowRoot(BaseElement):
         if loc[0] == 'css selector' and str(loc[1]).startswith(':root'):
             loc = loc[0], loc[1][5:]
 
-        result = None
-        timeout = timeout if timeout is not None else self.page.timeout
-        end_time = perf_counter() + timeout
-        while perf_counter() <= end_time:
+        def do_find():
             if loc[0] == 'css selector':
                 if single:
                     nod_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId']
-                    result = make_chromium_ele(self.page, node_id=nod_id) if nod_id else NoneElement(self.page)
+                    if nod_id:
+                        r = make_chromium_ele(self.page, node_id=nod_id)
+                        return None if r is False else r
 
                 else:
                     nod_ids = self.page.run_cdp('DOM.querySelectorAll', nodeId=self._node_id, selector=loc[1])['nodeId']
-                    result = make_chromium_eles(self.page, node_ids=nod_ids, single=False)
+                    r = make_chromium_eles(self.page, node_ids=nod_ids, single=False)
+                    return None if r is False else r
 
             else:
                 eles = make_session_ele(self.html).eles(loc)
                 if not eles:
-                    result = NoneElement(self.page) if single else eles
-                    continue
+                    return None
 
                 css = [i.css_path[61:] for i in eles]
                 if single:
                     node_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=css[0])['nodeId']
-                    result = make_chromium_ele(self.page, node_id=node_id) if node_id else NoneElement(self.page)
-
+                    r = make_chromium_ele(self.page, node_id=node_id)
+                    return None if r is False else r
                 else:
-                    result = []
-                    for i in css:
-                        node_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId']
-                        if node_id:
-                            result.append(make_chromium_ele(self.page, node_id=node_id))
+                    node_ids = [self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId']
+                                for i in css]
+                    if 0 in node_ids:
+                        return None
+                    r = make_chromium_eles(self.page, node_ids=node_ids, single=False)
+                    return None if r is False else r
 
-            if result:
-                break
+        timeout = timeout if timeout is not None else self.page.timeout
+        end_time = perf_counter() + timeout
+        result = do_find()
+        while result is None and perf_counter() <= end_time:
             sleep(.1)
+            result = do_find()
 
-        return result
+        if result:
+            return result
+        return NoneElement(self.page) if single else []
 
     def _get_node_id(self, obj_id):
         """返回元素node id"""
@@ -1087,7 +1091,7 @@ def find_in_chromium_ele(ele, loc, single=True, timeout=None, relative=True):
     :param ele: ChromiumElement对象
     :param loc: 元素定位元组
     :param single: True则返回第一个,False则返回全部
-    :param timeout: 查找元素超时时间
+    :param timeout: 查找元素超时时间(秒)
     :param relative: WebPage用于标记是否相对定位使用
     :return: 返回ChromiumElement元素或它们组成的列表
     """
@@ -1119,45 +1123,57 @@ def find_by_xpath(ele, xpath, single, timeout, relative=True):
     :param ele: 在此元素中查找
     :param xpath: 查找语句
     :param single: 是否只返回第一个结果
-    :param timeout: 超时时间
+    :param timeout: 超时时间(秒)
     :param relative: 是否相对定位
     :return: ChromiumElement或其组成的列表
     """
     type_txt = '9' if single else '7'
     node_txt = 'this.contentDocument' if ele.tag in __FRAME_ELEMENT__ and not relative else 'this'
     js = make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt)
-    r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
-                                returnByValue=False, awaitPromise=True, userGesture=True)
-    if r['result']['type'] == 'string':
-        return r['result']['value']
+    ele.page.wait.load_complete()
+
+    def do_find():
+        res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
+                               returnByValue=False, awaitPromise=True, userGesture=True)
+        if res['result']['type'] == 'string':
+            return res['result']['value']
+        if 'exceptionDetails' in res:
+            if 'The result is not a node set' in res['result']['description']:
+                js1 = make_js_for_find_ele_by_xpath(xpath, '1', node_txt)
+                res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js1, objectId=ele._obj_id,
+                                       returnByValue=False, awaitPromise=True, userGesture=True)
+                return res['result']['value']
+            else:
+                raise SyntaxError(f'查询语句错误:\n{res}')
+
+        if res['result']['subtype'] == 'null' or res['result']['description'] in ('NodeList(0)', 'Array(0)'):
+            return None
+
+        if single:
+            r = make_chromium_ele(ele.page, obj_id=res['result']['objectId'])
+            return None if r is False else r
 
-    if 'exceptionDetails' in r:
-        if 'The result is not a node set' in r['result']['description']:
-            js = make_js_for_find_ele_by_xpath(xpath, '1', node_txt)
-            r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
-                                        returnByValue=False, awaitPromise=True, userGesture=True)
-            return r['result']['value']
         else:
-            raise SyntaxError(f'查询语句错误:\n{r}')
+            # from pprint import pprint
+            # for i in ele.page.run_cdp('Runtime.getProperties',
+            #                           objectId=res['result']['objectId'],
+            #                           ownProperties=True)['result'][:-1]:
+            #     pprint(i)
+            r = [make_chromium_ele(ele.page, obj_id=i['value']['objectId']) if i['value']['type'] == 'object' else
+                 i['value']['value'] for i in ele.page.run_cdp('Runtime.getProperties',
+                                                               objectId=res['result']['objectId'],
+                                                               ownProperties=True)['result'][:-1]]
+            return None if not r or r is False in r else r
 
     end_time = perf_counter() + timeout
-    while ((r['result']['subtype'] == 'null' or r['result']['description'] in ('NodeList(0)', 'Array(0)'))
-           and perf_counter() < end_time):
-        r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
-                                    returnByValue=False, awaitPromise=True, userGesture=True)
+    result = do_find()
+    while result is None and perf_counter() < end_time:
+        sleep(.1)
+        result = do_find()
 
-    if single:
-        return NoneElement(ele.page) if r['result']['subtype'] == 'null' \
-            else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
-
-    if r['result']['description'] in ('NodeList(0)', 'Array(0)'):
-        return []
-    else:
-        r = ele.page.run_cdp_loaded('Runtime.getProperties', objectId=r['result']['objectId'],
-                                    ownProperties=True)['result']
-        return [make_chromium_ele(ele.page, obj_id=i['value']['objectId'])
-                if i['value']['type'] == 'object' else i['value']['value']
-                for i in r[:-1]]
+    if result:
+        return result
+    return NoneElement(ele.page) if single else []
 
 
 def find_by_css(ele, selector, single, timeout):
@@ -1165,35 +1181,45 @@ def find_by_css(ele, selector, single, timeout):
     :param ele: 在此元素中查找
     :param selector: 查找语句
     :param single: 是否只返回第一个结果
-    :param timeout: 超时时间
+    :param timeout: 超时时间(秒)
     :return: ChromiumElement或其组成的列表
     """
     selector = selector.replace('"', r'\"')
     find_all = '' if single else 'All'
     node_txt = 'this.contentDocument' if ele.tag in ('iframe', 'frame', 'shadow-root') else 'this'
     js = f'function(){{return {node_txt}.querySelector{find_all}("{selector}");}}'
-    r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
-                                returnByValue=False, awaitPromise=True, userGesture=True)
+
+    ele.page.wait.load_complete()
+
+    def do_find():
+        res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
+                               returnByValue=False, awaitPromise=True, userGesture=True)
+
+        if 'exceptionDetails' in res:
+            raise SyntaxError(f'查询语句错误:\n{res}')
+        if res['result']['subtype'] == 'null' or res['result']['description'] in ('NodeList(0)', 'Array(0)'):
+            return None
+
+        if single:
+            r = make_chromium_ele(ele.page, obj_id=res['result']['objectId'])
+            return None if r is False else r
+
+        else:
+            node_ids = [i['value']['objectId'] for i in ele.page.run_cdp('Runtime.getProperties',
+                                                                         objectId=res['result']['objectId'],
+                                                                         ownProperties=True)['result'][:-1]]
+            r = make_chromium_eles(ele.page, obj_ids=node_ids, single=False, ele_only=False)
+            return None if r is False else r
 
     end_time = perf_counter() + timeout
-    while ('exceptionDetails' in r or r['result']['subtype'] == 'null' or
-           r['result']['description'] in ('NodeList(0)', 'Array(0)')) and perf_counter() < end_time:
-        r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
-                                    returnByValue=False, awaitPromise=True, userGesture=True)
+    result = do_find()
+    while result is None and perf_counter() < end_time:
+        sleep(.1)
+        result = do_find()
 
-    if 'exceptionDetails' in r:
-        raise SyntaxError(f'查询语句错误:\n{r}')
-
-    if single:
-        return NoneElement(ele.page) if r['result']['subtype'] == 'null' \
-            else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
-
-    if r['result']['description'] in ('NodeList(0)', 'Array(0)'):
-        return []
-    else:
-        r = ele.page.run_cdp_loaded('Runtime.getProperties', objectId=r['result']['objectId'],
-                                    ownProperties=True)['result']
-        return [make_chromium_ele(ele.page, obj_id=i['value']['objectId']) for i in r]
+    if result:
+        return result
+    return NoneElement(ele.page) if single else []
 
 
 def make_chromium_ele(page, node_id=None, obj_id=None):
@@ -1201,18 +1227,24 @@ def make_chromium_ele(page, node_id=None, obj_id=None):
     :param page: ChromiumPage对象
     :param node_id: 元素的node id
     :param obj_id: 元素的object id
-    :return: ChromiumElement对象或ChromiumFrame对象
+    :return: ChromiumElement对象或ChromiumFrame对象,生成失败返回False
     """
     if node_id:
-        node = page.run_cdp('DOM.describeNode', nodeId=node_id)
+        node = page.driver.run('DOM.describeNode', nodeId=node_id)
+        if 'error' in node:
+            return False
         if node['node']['nodeName'] in ('#text', '#comment'):
             # todo: Node()
             return node['node']['nodeValue']
         backend_id = node['node']['backendNodeId']
         obj_id = page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId']
+        if 'error' in obj_id:
+            return False
 
     elif obj_id:
-        node = page.run_cdp('DOM.describeNode', objectId=obj_id)
+        node = page.driver.run('DOM.describeNode', objectId=obj_id)
+        if 'error' in node:
+            return False
         if node['node']['nodeName'] in ('#text', '#comment'):
             # todo: Node()
             return node['node']['nodeValue']
@@ -1220,12 +1252,12 @@ def make_chromium_ele(page, node_id=None, obj_id=None):
         node_id = node['node']['nodeId']
 
     else:
-        raise ElementLostError
+        return False
 
     ele = ChromiumElement(page, obj_id=obj_id, node_id=node_id, backend_id=backend_id)
     if ele.tag in __FRAME_ELEMENT__:
         from .._pages.chromium_frame import ChromiumFrame
-        ele = ChromiumFrame(page, ele)
+        ele = ChromiumFrame(page, ele, node)
 
     return ele
 
@@ -1237,14 +1269,16 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only=
     :param obj_ids: 元素的object id
     :param single: 是否获取但个元素
     :param ele_only: 是否只要ele
-    :return: ChromiumElement对象或ChromiumFrame对象
+    :return: ChromiumElement对象或ChromiumFrame对象,生成失败返回False
     """
     nodes = []
     if node_ids:
         for node_id in node_ids:
             if not node_id:
                 return False
-            node = page.run_cdp('DOM.describeNode', nodeId=node_id)
+            node = page.driver.run('DOM.describeNode', nodeId=node_id)
+            if 'error' in node:
+                return False
             if node['node']['nodeName'] in ('#text', '#comment'):
                 if ele_only:
                     continue
@@ -1254,11 +1288,14 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only=
                     else:
                         nodes.append(node['node']['nodeValue'])
 
-            obj_id = page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId']
+            obj_id = page.driver.run('DOM.resolveNode', nodeId=node_id)
+            if 'error' in obj_id:
+                return False
+            obj_id = obj_id['object']['objectId']
             ele = ChromiumElement(page, obj_id=obj_id, node_id=node_id, backend_id=node['node']['backendNodeId'])
             if ele.tag in __FRAME_ELEMENT__:
                 from .._pages.chromium_frame import ChromiumFrame
-                ele = ChromiumFrame(page, ele)
+                ele = ChromiumFrame(page, ele, node)
             if single:
                 return ele
             nodes.append(ele)
@@ -1267,7 +1304,9 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only=
         for obj_id in obj_ids:
             if not obj_id:
                 return False
-            node = page.run_cdp('DOM.describeNode', objectId=obj_id)
+            node = page.driver.run('DOM.describeNode', objectId=obj_id)
+            if 'error' in node:
+                return False
             if node['node']['nodeName'] in ('#text', '#comment'):
                 if ele_only:
                     continue
@@ -1281,7 +1320,7 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only=
                                   backend_id=node['node']['backendNodeId'])
             if ele.tag in __FRAME_ELEMENT__:
                 from .._pages.chromium_frame import ChromiumFrame
-                ele = ChromiumFrame(page, ele)
+                ele = ChromiumFrame(page, ele, node)
             if single:
                 return ele
             nodes.append(ele)
@@ -1336,7 +1375,7 @@ def run_js(page_or_ele, script, as_expr=False, timeout=None, args=None):
     :param page_or_ele: 页面对象或元素对象
     :param script: js文本
     :param as_expr: 是否作为表达式运行,为True时args无效
-    :param timeout: 超时时间
+    :param timeout: 超时时间(秒)
     :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
     :return: js执行结果
     """
@@ -1409,16 +1448,17 @@ def parse_js_result(page, ele, result):
             elif class_name == 'HTMLDocument':
                 return result
             else:
-                return make_chromium_eles(page, obj_ids=(result['objectId'],))
+                r = make_chromium_ele(page, obj_id=result['objectId'])
+                if r is False:
+                    raise ElementLostError
+                return r
 
         elif sub_type == 'array':
-            r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'],
-                             ownProperties=True)['result']
+            r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result']
             return [parse_js_result(page, ele, result=i['value']) for i in r[:-1]]
 
         elif 'objectId' in result and result['className'].lower() == 'object':  # dict
-            r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'],
-                             ownProperties=True)['result']
+            r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result']
             return {i['name']: parse_js_result(page, ele, result=i['value']) for i in r}
 
         else:
diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi
index b381132..44b6c8d 100644
--- a/DrissionPage/_elements/chromium_element.pyi
+++ b/DrissionPage/_elements/chromium_element.pyi
@@ -99,19 +99,19 @@ class ChromiumElement(DrissionElement):
     def child(self,
               filter_loc: Union[tuple, str, int] = '',
               index: int = 1,
-              timeout: float = 0,
+              timeout: float = None,
               ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
 
     def prev(self,
              filter_loc: Union[tuple, str, int] = '',
              index: int = 1,
-             timeout: float = 0,
+             timeout: float = None,
              ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
 
     def next(self,
              filter_loc: Union[tuple, str, int] = '',
              index: int = 1,
-             timeout: float = 0,
+             timeout: float = None,
              ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ...
 
     def before(self,
@@ -128,17 +128,17 @@ class ChromiumElement(DrissionElement):
 
     def children(self,
                  filter_loc: Union[tuple, str] = '',
-                 timeout: float = 0,
+                 timeout: float = None,
                  ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
 
     def prevs(self,
               filter_loc: Union[tuple, str] = '',
-              timeout: float = 0,
+              timeout: float = None,
               ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
 
     def nexts(self,
               filter_loc: Union[tuple, str] = '',
-              timeout: float = 0,
+              timeout: float = None,
               ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ...
 
     def befores(self,
@@ -315,12 +315,12 @@ def find_by_css(ele: ChromiumElement, selector: str, single: bool,
                 timeout: float) -> Union[ChromiumElement, List[ChromiumElement], NoneElement]: ...
 
 
-def make_chromium_ele(page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
+def make_chromium_ele(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
                       node_id: int = ...,
                       obj_id: str = ...) -> Union[ChromiumElement, ChromiumFrame, str]: ...
 
 
-def make_chromium_eles(page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
+def make_chromium_eles(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
                        node_ids: Union[tuple, list] = None,
                        obj_ids: Union[tuple, list] = None,
                        single: bool = True,
diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py
index f5f4cca..33f0df6 100644
--- a/DrissionPage/_functions/browser.py
+++ b/DrissionPage/_functions/browser.py
@@ -194,7 +194,7 @@ def test_connect(ip, port, timeout=30):
     """测试浏览器是否可用
     :param ip: 浏览器ip
     :param port: 浏览器端口
-    :param timeout: 超时时间
+    :param timeout: 超时时间(秒)
     :return: None
     """
     end_time = perf_counter() + timeout
diff --git a/DrissionPage/_functions/keys.py b/DrissionPage/_functions/keys.py
index 7e4a300..8c0a243 100644
--- a/DrissionPage/_functions/keys.py
+++ b/DrissionPage/_functions/keys.py
@@ -419,7 +419,7 @@ def send_key(page, modifier, key):
                 'unmodifiedText': text,
                 'location': description['location'],
                 'isKeypad': description['location'] == 3,
-                '_ignore': AlertExistsError, '_timeout': 1}
+                '_ignore': AlertExistsError}
 
         page.run_cdp('Input.dispatchKeyEvent', **data)
         data['type'] = 'keyUp'
diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py
index 6ab1229..4db38b5 100644
--- a/DrissionPage/_functions/tools.py
+++ b/DrissionPage/_functions/tools.py
@@ -192,7 +192,7 @@ def wait_until(page, condition, timeout=10, poll=0.1, raise_err=True):
     """等待返回值不为False或空,直到超时
     :param page: DrissionPage对象
     :param condition: 等待条件,返回值不为False则停止等待
-    :param timeout: 超时时间
+    :param timeout: 超时时间(秒)
     :param poll: 轮询间隔
     :param raise_err: 是否抛出异常
     :return: DP Element or bool
@@ -267,7 +267,7 @@ def raise_error(result, ignore=None):
                    'No node with given id found', 'Node with given id does not belong to the document',
                    'No node found for given backend id'):
         r = ElementLostError()
-    elif error in ('tab closed', 'No target with given id found'):
+    elif error in ('page closed', 'No target with given id found'):
         r = PageClosedError()
     elif error == 'timeout':
         r = TimeoutError(f'超时。\n错误:{result["error"]}\nmethod:{result["method"]}\nargs:{result["args"]}\n'
diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py
index 12eb386..eb21aee 100644
--- a/DrissionPage/_pages/chromium_base.py
+++ b/DrissionPage/_pages/chromium_base.py
@@ -38,7 +38,7 @@ class ChromiumBase(BasePage):
         """
         :param address: 浏览器 ip:port
         :param tab_id: 要控制的标签页id,不指定默认为激活的
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         """
         super().__init__()
         self._is_loading = None
@@ -147,7 +147,7 @@ class ChromiumBase(BasePage):
 
     def _get_document(self, timeout=10):
         """获取页面文档
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 是否获取成功
         """
         if self._debug:
@@ -282,7 +282,7 @@ class ChromiumBase(BasePage):
         """在内部查找元素
         例:ele = page('@id=ele_id')
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: ChromiumElement对象
         """
         return self.ele(loc_or_str, timeout)
@@ -468,7 +468,7 @@ class ChromiumBase(BasePage):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: 运行的结果
         """
         return run_js(self, script, as_expr, self.timeouts.script if timeout is None else timeout, args)
@@ -478,7 +478,7 @@ class ChromiumBase(BasePage):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: 运行的结果
         """
         self.wait.load_complete()
@@ -489,7 +489,7 @@ class ChromiumBase(BasePage):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: None
         """
         from threading import Thread
@@ -502,7 +502,7 @@ class ChromiumBase(BasePage):
         :param show_errmsg: 是否显示和抛出异常
         :param retry: 重试次数
         :param interval: 重试间隔(秒)
-        :param timeout: 连接超时时间
+        :param timeout: 连接超时时间(秒)
         :return: 目标url是否可用
         """
         retry, interval = self._before_connect(url, retry, interval)
@@ -531,7 +531,7 @@ class ChromiumBase(BasePage):
     def ele(self, loc_or_ele, timeout=None):
         """获取第一个符合条件的元素对象
         :param loc_or_ele: 定位符或元素对象
-        :param timeout: 查找超时时间
+        :param timeout: 查找超时时间(秒)
         :return: ChromiumElement对象
         """
         return self._ele(loc_or_ele, timeout=timeout, method='ele()')
@@ -539,7 +539,7 @@ class ChromiumBase(BasePage):
     def eles(self, loc_or_str, timeout=None):
         """获取所有符合条件的元素对象
         :param loc_or_str: 定位符或元素对象
-        :param timeout: 查找超时时间
+        :param timeout: 查找超时时间(秒)
         :return: ChromiumElement对象组成的列表
         """
         return self._ele(loc_or_str, timeout=timeout, single=False)
@@ -568,7 +568,7 @@ class ChromiumBase(BasePage):
     def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None):
         """执行元素查找
         :param loc_or_ele: 定位符或元素对象
-        :param timeout: 查找超时时间
+        :param timeout: 查找超时时间(秒)
         :param single: 是否只返回第一个
         :param relative: WebPage用的表示是否相对定位的参数
         :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置
@@ -581,56 +581,39 @@ class ChromiumBase(BasePage):
         else:
             raise ValueError('loc_or_str参数只能是tuple、str、ChromiumElement类型。')
 
-        ok = False
-        nodeIds = None
-
+        self.wait.load_complete()
         timeout = timeout if timeout is not None else self.timeout
         end_time = perf_counter() + timeout
 
         search_ids = []
-        try:
-            search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, _timeout=timeout,
-                                                includeUserAgentShadowDOM=True)
-            count = search_result['resultCount']
-            search_ids.append(search_result['searchId'])
-        except ContextLostError:
-            search_result = None
-            count = 0
+        timeout = .5 if timeout <= 0 else timeout
+        result = self.driver.run('DOM.performSearch', query=loc, _timeout=timeout, includeUserAgentShadowDOM=True)
+        if not result or __ERROR__ in result:
+            num = 0
+        else:
+            num = result['resultCount']
+            search_ids.append(result['searchId'])
 
         while True:
-            if count > 0:
-                count = 1 if single else count
-                try:
-                    nodeIds = self.run_cdp_loaded('DOM.getSearchResults', searchId=search_result['searchId'],
-                                                  fromIndex=0, toIndex=count)
-                    if nodeIds['nodeIds'][0] != 0:
-                        ok = True
-
-                except Exception:
-                    pass
-
-            if ok:
-                r = make_chromium_eles(self, node_ids=nodeIds['nodeIds'], single=single)
-                if r is not False:
-                    break
-                else:
-                    ok = False
-
-            try:
-                timeout = end_time - perf_counter()
-                if timeout <= 0:
-                    timeout = .5
-                search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, _timeout=timeout,
-                                                    includeUserAgentShadowDOM=True)
-                count = search_result['resultCount']
-                search_ids.append(search_result['searchId'])
-            except ContextLostError:
-                pass
+            if num > 0:
+                num = 1 if single else num
+                nIds = self._driver.run('DOM.getSearchResults', searchId=result['searchId'], fromIndex=0, toIndex=num)
+                if __ERROR__ not in nIds:
+                    if nIds['nodeIds'][0] != 0:
+                        r = make_chromium_eles(self, node_ids=nIds['nodeIds'], single=single)
+                        if r is not False:
+                            break
 
             if perf_counter() >= end_time:
                 return NoneElement(self) if single else []
 
             sleep(.1)
+            timeout = end_time - perf_counter()
+            timeout = .5 if timeout <= 0 else timeout
+            result = self.driver.run('DOM.performSearch', query=loc, _timeout=timeout, includeUserAgentShadowDOM=True)
+            if not result or __ERROR__ not in result:
+                num = result['resultCount']
+                search_ids.append(result['searchId'])
 
         for _id in search_ids:
             self._driver.run('DOM.discardSearchResults', searchId=_id)
@@ -712,7 +695,7 @@ class ChromiumBase(BasePage):
     def get_frame(self, loc_ind_ele, timeout=None):
         """获取页面中一个frame对象,可传入定位符、iframe序号、ChromiumFrame对象,序号从1开始
         :param loc_ind_ele: 定位符、iframe序号、ChromiumFrame对象
-        :param timeout: 查找元素超时时间
+        :param timeout: 查找元素超时时间(秒)
         :return: ChromiumFrame对象
         """
         if isinstance(loc_ind_ele, str):
@@ -752,7 +735,7 @@ class ChromiumBase(BasePage):
     def get_frames(self, loc=None, timeout=None):
         """获取所有符合条件的frame对象
         :param loc: 定位符,为None时返回所有
-        :param timeout: 查找超时时间
+        :param timeout: 查找超时时间(秒)
         :return: ChromiumFrame对象组成的列表
         """
         loc = loc or 'xpath://*[name()="iframe" or name()="frame"]'
@@ -839,6 +822,11 @@ class ChromiumBase(BasePage):
         if cookies:
             self.run_cdp_loaded('Network.clearBrowserCookies')
 
+    def disconnect(self):
+        """断开与页面的连接,不关闭页面"""
+        if self._driver:
+            self.driver.stop()
+
     def handle_alert(self, accept=True, send=None, timeout=None, next_one=False):
         r = self._handle_alert(accept=accept, send=send, timeout=timeout, next_one=next_one)
         while self._has_alert:
@@ -849,7 +837,7 @@ class ChromiumBase(BasePage):
         """处理提示框,可以自动等待提示框出现
         :param accept: True表示确认,False表示取消,其它值不会按按钮但依然返回文本值
         :param send: 处理prompt提示框时可输入文本
-        :param timeout: 等待提示框出现的超时时间,为None则使用self.timeout属性的值
+        :param timeout: 等待提示框出现的超时时间(秒),为None则使用self.timeout属性的值
         :param next_one: 是否处理下一个出现的提示框,为True时timeout参数无效
         :return: 提示框内容文本,未等到提示框则返回False
         """
@@ -901,12 +889,9 @@ class ChromiumBase(BasePage):
 
     def _wait_loaded(self, timeout=None):
         """等待页面加载完成,超时触发停止加载
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 是否成功,超时返回False
         """
-        if self._load_mode == 'none':
-            return True
-
         timeout = timeout if timeout is not None else self.timeouts.page_load
         end_time = perf_counter() + timeout
         while perf_counter() < end_time:
@@ -948,10 +933,11 @@ class ChromiumBase(BasePage):
         :param times: 重试次数
         :param interval: 重试间隔(秒)
         :param show_errmsg: 是否抛出异常
-        :param timeout: 连接超时时间
+        :param timeout: 连接超时时间(秒)
         :return: 是否成功,返回None表示不确定
         """
         err = None
+        self._is_loading = True
         timeout = timeout if timeout is not None else self.timeouts.page_load
         for t in range(times + 1):
             err = None
diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi
index 98c98fb..5781211 100644
--- a/DrissionPage/_pages/chromium_base.pyi
+++ b/DrissionPage/_pages/chromium_base.pyi
@@ -224,6 +224,8 @@ class ChromiumBase(BasePage):
     def clear_cache(self, session_storage: bool = True, local_storage: bool = True, cache: bool = True,
                     cookies: bool = True) -> None: ...
 
+    def disconnect(self) -> None: ...
+
     def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None,
                      next_one: bool = False) -> Union[str, False]: ...
 
diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py
index 873f331..f2d8820 100644
--- a/DrissionPage/_pages/chromium_frame.py
+++ b/DrissionPage/_pages/chromium_frame.py
@@ -19,10 +19,11 @@ from ..errors import ContextLostError, ElementLostError, GetDocumentError, PageC
 
 
 class ChromiumFrame(ChromiumBase):
-    def __init__(self, page, ele):
+    def __init__(self, page, ele, info=None):
         """
         :param page: frame所在的页面对象
         :param ele: frame所在元素
+        :param info: frame所在元素信息
         """
         page_type = str(type(page))
         if 'ChromiumPage' in page_type or 'WebPage' in page_type:
@@ -41,7 +42,7 @@ class ChromiumFrame(ChromiumBase):
         self._states = None
         self._reloading = False
 
-        node = page.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node']
+        node = info['node'] if not info else page.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node']
         self._frame_id = node['frameId']
         if self._is_inner_frame():
             self._is_diff_domain = False
@@ -65,7 +66,7 @@ class ChromiumFrame(ChromiumBase):
         """在内部查找元素
         例:ele2 = ele1('@id=ele_id')
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: ChromiumElement对象或属性、文本
         """
         return self.ele(loc_or_str, timeout)
@@ -159,7 +160,7 @@ class ChromiumFrame(ChromiumBase):
 
     def _get_document(self, timeout=10):
         """刷新cdp使用的document数据
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 是否获取成功
         """
         if self._is_reading:
@@ -392,7 +393,7 @@ class ChromiumFrame(ChromiumBase):
         :param script: js文本
         :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]...
         :param as_expr: 是否作为表达式运行,为True时args无效
-        :param timeout: js超时时间,为None则使用页面timeouts.script设置
+        :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置
         :return: 运行的结果
         """
         if script.startswith('this.scrollIntoView'):
@@ -412,7 +413,7 @@ class ChromiumFrame(ChromiumBase):
         """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 同级元素或节点
         """
@@ -422,7 +423,7 @@ class ChromiumFrame(ChromiumBase):
         """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个
         :param filter_loc: 用于筛选的查询语法
         :param index: 后面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 同级元素或节点
         """
@@ -433,7 +434,7 @@ class ChromiumFrame(ChromiumBase):
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
         :param index: 前面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素前面的某个元素或节点
         """
@@ -444,7 +445,7 @@ class ChromiumFrame(ChromiumBase):
         查找范围不限同级元素,而是整个DOM文档
         :param filter_loc: 用于筛选的查询语法
         :param index: 后面第几个查询结果,1开始
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 本元素后面的某个元素或节点
         """
@@ -453,7 +454,7 @@ class ChromiumFrame(ChromiumBase):
     def prevs(self, filter_loc='', timeout=0, ele_only=True):
         """返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 同级元素或节点文本组成的列表
         """
@@ -462,7 +463,7 @@ class ChromiumFrame(ChromiumBase):
     def nexts(self, filter_loc='', timeout=0, ele_only=True):
         """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选
         :param filter_loc: 用于筛选的查询语法
-        :param timeout: 查找节点的超时时间
+        :param timeout: 查找节点的超时时间(秒)
         :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入
         :return: 同级元素或节点文本组成的列表
         """
diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi
index 8dc1380..f2cf074 100644
--- a/DrissionPage/_pages/chromium_frame.pyi
+++ b/DrissionPage/_pages/chromium_frame.pyi
@@ -24,7 +24,8 @@ class ChromiumFrame(ChromiumBase):
 
     def __init__(self,
                  page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame],
-                 ele: ChromiumElement):
+                 ele: ChromiumElement,
+                 info: dict = None):
         self._page: ChromiumPage = ...
         self._target_page: ChromiumBase = ...
         self.tab: ChromiumTab = ...
diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py
index 2c53511..73777de 100644
--- a/DrissionPage/_pages/chromium_page.py
+++ b/DrissionPage/_pages/chromium_page.py
@@ -25,7 +25,7 @@ class ChromiumPage(ChromiumBase):
         """
         :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int)
         :param tab_id: 要控制的标签页id,不指定默认为激活的
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         """
         addr_or_opts = addr_or_opts or addr_driver_opts
         self._page = self
@@ -242,7 +242,7 @@ class ChromiumPage(ChromiumBase):
 
     def quit(self, timeout=5, force=True):
         """关闭浏览器
-        :param timeout: 等待浏览器关闭超时时间
+        :param timeout: 等待浏览器关闭超时时间(秒)
         :param force: 关闭超时是否强制终止进程
         :return: None
         """
diff --git a/DrissionPage/_pages/chromium_tab.py b/DrissionPage/_pages/chromium_tab.py
index 983ee4a..dacf435 100644
--- a/DrissionPage/_pages/chromium_tab.py
+++ b/DrissionPage/_pages/chromium_tab.py
@@ -79,7 +79,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
         """在内部查找元素
         例:ele = page('@id=ele_id')
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 子元素对象
         """
         if self._mode == 'd':
@@ -221,7 +221,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
     def ele(self, loc_or_ele, timeout=None):
         """返回第一个符合条件的元素、属性或节点文本
         :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致
         :return: 元素对象或属性、文本节点文本
         """
         if self._mode == 's':
@@ -232,7 +232,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
     def eles(self, loc_or_str, timeout=None):
         """返回页面中所有符合条件的元素、属性或节点文本
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致
         :return: 元素对象或属性、文本组成的列表
         """
         if self._mode == 's':
@@ -345,7 +345,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage):
     def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None):
         """返回页面中符合条件的元素、属性或节点文本,默认返回第一个
         :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,d模式专用
+        :param timeout: 查找元素超时时间(秒),d模式专用
         :param single: True则返回第一个,False则返回全部
         :param relative: WebPage用的表示是否相对定位的参数
         :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置
diff --git a/DrissionPage/_pages/session_page.py b/DrissionPage/_pages/session_page.py
index 9dfcbf5..0170a1c 100644
--- a/DrissionPage/_pages/session_page.py
+++ b/DrissionPage/_pages/session_page.py
@@ -25,7 +25,7 @@ class SessionPage(BasePage):
     def __init__(self, session_or_options=None, timeout=None):
         """
         :param session_or_options: Session对象或SessionOptions对象
-        :param timeout: 连接超时时间,为None时从ini文件读取
+        :param timeout: 连接超时时间(秒),为None时从ini文件读取
         """
         super(SessionPage, SessionPage).__init__(self)
         self._headers = None
diff --git a/DrissionPage/_pages/web_page.py b/DrissionPage/_pages/web_page.py
index a62d30c..22940e2 100644
--- a/DrissionPage/_pages/web_page.py
+++ b/DrissionPage/_pages/web_page.py
@@ -18,7 +18,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
     def __init__(self, mode='d', timeout=None, chromium_options=None, session_or_options=None, driver_or_options=None):
         """初始化函数
         :param mode: 'd' 或 's',即driver模式和session模式
-        :param timeout: 超时时间,d模式时为寻找元素时间,s模式时为连接时间,默认10秒
+        :param timeout: 超时时间(秒),d模式时为寻找元素时间,s模式时为连接时间,默认10秒
         :param chromium_options: Driver对象,只使用s模式时应传入False
         :param session_or_options: Session对象或SessionOptions对象,只使用d模式时应传入False
         """
@@ -40,7 +40,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
         """在内部查找元素
         例:ele = page('@id=ele_id')
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 超时时间
+        :param timeout: 超时时间(秒)
         :return: 子元素对象
         """
         if self._mode == 'd':
@@ -182,7 +182,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
     def ele(self, loc_or_ele, timeout=None):
         """返回第一个符合条件的元素、属性或节点文本
         :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致
         :return: 元素对象或属性、文本节点文本
         """
         if self._mode == 's':
@@ -193,7 +193,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
     def eles(self, loc_or_str, timeout=None):
         """返回页面中所有符合条件的元素、属性或节点文本
         :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
-        :param timeout: 查找元素超时时间,默认与页面等待时间一致
+        :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致
         :return: 元素对象或属性、文本组成的列表
         """
         if self._mode == 's':
diff --git a/DrissionPage/_units/actions.py b/DrissionPage/_units/actions.py
index 4f4b282..d8a90c3 100644
--- a/DrissionPage/_units/actions.py
+++ b/DrissionPage/_units/actions.py
@@ -252,7 +252,6 @@ class Actions:
             return self
 
         data = self._get_key_data(key, 'keyDown')
-        data['_timeout'] = 1
         data['_ignore'] = AlertExistsError
         self.page.run_cdp('Input.dispatchKeyEvent', **data)
         return self
@@ -268,7 +267,6 @@ class Actions:
             return self
 
         data = self._get_key_data(key, 'keyUp')
-        data['_timeout'] = 1
         data['_ignore'] = AlertExistsError
         self.page.run_cdp('Input.dispatchKeyEvent', **data)
         return self
diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py
index 9a85215..6565e50 100644
--- a/DrissionPage/_units/clicker.py
+++ b/DrissionPage/_units/clicker.py
@@ -21,7 +21,7 @@ class Clicker(object):
         """点击元素
         如果遇到遮挡,可选择是否用js点击
         :param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击
-        :param timeout: 模拟点击的超时时间,等待元素可见、可用、进入视口
+        :param timeout: 模拟点击的超时时间(秒),等待元素可见、可用、进入视口
         :param wait_stop: 是否等待元素运动结束再执行点击
         :return: 是否点击成功
         """
@@ -30,7 +30,7 @@ class Clicker(object):
     def left(self, by_js=False, timeout=1.5, wait_stop=True):
         """点击元素,可选择是否用js点击
         :param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击
-        :param timeout: 模拟点击的超时时间,等待元素可见、可用、进入视口
+        :param timeout: 模拟点击的超时时间(秒),等待元素可见、可用、进入视口
         :param wait_stop: 是否等待元素运动结束再执行点击
         :return: 是否点击成功
         """