解决新加载的iframe点击问题;对无位置和大小信息的元素进行处理

This commit is contained in:
g1879 2023-02-17 19:52:37 +08:00
parent e8b22de2e3
commit 5dce3077ed
4 changed files with 40 additions and 32 deletions

View File

@ -15,7 +15,8 @@ from .chromium_driver import ChromiumDriver
from .chromium_element import ChromiumWaiter, ChromiumScroll, ChromiumElement, run_js, make_chromium_ele, \ from .chromium_element import ChromiumWaiter, ChromiumScroll, ChromiumElement, run_js, make_chromium_ele, \
ChromiumElementWaiter ChromiumElementWaiter
from .common.constants import HANDLE_ALERT_METHOD, ERROR, NoneElement from .common.constants import HANDLE_ALERT_METHOD, ERROR, NoneElement
from .common.errors import ContextLossError, ElementLossError, AlertExistsError, CallMethodError, TabClosedError from .common.errors import ContextLossError, ElementLossError, AlertExistsError, CallMethodError, TabClosedError, \
NoRectError
from .common.locator import get_loc from .common.locator import get_loc
from .common.tools import get_usable_path from .common.tools import get_usable_path
from .common.web import offset_scroll, cookies_to_tuple from .common.web import offset_scroll, cookies_to_tuple
@ -346,6 +347,8 @@ class ChromiumBase(BasePage):
raise TabClosedError raise TabClosedError
elif r[ERROR] == 'alert exists': elif r[ERROR] == 'alert exists':
pass pass
elif r[ERROR] in ('Node does not have a layout object', 'Could not compute box model.'):
raise NoRectError
elif r['type'] == 'call_method_error': elif r['type'] == 'call_method_error':
raise CallMethodError(f'\n错误:{r["error"]}\nmethod{r["method"]}\nargs{r["args"]}') raise CallMethodError(f'\n错误:{r["error"]}\nmethod{r["method"]}\nargs{r["args"]}')
else: else:
@ -924,7 +927,7 @@ class ChromiumPageScroll(ChromiumScroll):
""" """
ele = self._driver.ele(loc_or_ele) ele = self._driver.ele(loc_or_ele)
try: try:
self._driver.run_cdp('DOM.scrollIntoViewIfNeeded', nodeId=ele.node_id) self._driver.run_cdp('DOM.scrollIntoViewIfNeeded', nodeId=ele.ids.node_id)
except Exception: except Exception:
ele.run_js("this.scrollIntoView();") ele.run_js("this.scrollIntoView();")

View File

@ -11,7 +11,7 @@ from warnings import warn
from .base import DrissionElement, BaseElement from .base import DrissionElement, BaseElement
from .common.constants import FRAME_ELEMENT, NoneElement from .common.constants import FRAME_ELEMENT, NoneElement
from .common.errors import ContextLossError, ElementLossError, JavaScriptError from .common.errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError
from .common.locator import get_loc from .common.locator import get_loc
from .common.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll from .common.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll
from .keys import _keys_to_typing, _keyDescriptionForString, _keyDefinitions from .keys import _keys_to_typing, _keyDescriptionForString, _keyDefinitions
@ -1515,37 +1515,37 @@ class Locations(object):
def location(self): def location(self):
"""返回元素左上角的绝对坐标""" """返回元素左上角的绝对坐标"""
cl = self.viewport_location cl = self.viewport_location
return self._get_page_coord(cl[0], cl[1]) if cl else (0, 0) return self._get_page_coord(cl[0], cl[1])
@property @property
def midpoint(self): def midpoint(self):
"""返回元素中间点的绝对坐标""" """返回元素中间点的绝对坐标"""
cl = self.viewport_midpoint cl = self.viewport_midpoint
return self._get_page_coord(cl[0], cl[1]) if cl else (0, 0) return self._get_page_coord(cl[0], cl[1])
@property @property
def click_point(self): def click_point(self):
"""返回元素接受点击的点的绝对坐标""" """返回元素接受点击的点的绝对坐标"""
cl = self.viewport_click_point cl = self.viewport_click_point
return self._get_page_coord(cl[0], cl[1]) if cl else (0, 0) return self._get_page_coord(cl[0], cl[1])
@property @property
def viewport_location(self): def viewport_location(self):
"""返回元素左上角在视口中的坐标""" """返回元素左上角在视口中的坐标"""
m = self._get_viewport_rect('border') m = self._get_viewport_rect('border')
return (int(m[0]), int(m[1])) if m else (0, 0) return int(m[0]), int(m[1])
@property @property
def viewport_midpoint(self): def viewport_midpoint(self):
"""返回元素中间点在视口中的坐标""" """返回元素中间点在视口中的坐标"""
m = self._get_viewport_rect('border') m = self._get_viewport_rect('border')
return (int(m[0] + (m[2] - m[0]) // 2), int(m[3] + (m[5] - m[3]) // 2)) if m else (0, 0) return int(m[0] + (m[2] - m[0]) // 2), int(m[3] + (m[5] - m[3]) // 2)
@property @property
def viewport_click_point(self): def viewport_click_point(self):
"""返回元素接受点击的点视口坐标""" """返回元素接受点击的点视口坐标"""
m = self._get_viewport_rect('padding') m = self._get_viewport_rect('padding')
return (int(self.viewport_midpoint[0]), int(m[1]) + 1) if m else (0, 0) return int(self.viewport_midpoint[0]), int(m[1]) + 1
@property @property
def screen_location(self): def screen_location(self):
@ -1573,10 +1573,7 @@ class Locations(object):
:param quad: 方框类型margin border padding :param quad: 方框类型margin border padding
:return: 四个角坐标大小为0时返回None :return: 四个角坐标大小为0时返回None
""" """
try: return self._ele.page.run_cdp('DOM.getBoxModel', nodeId=self._ele.ids.node_id)['model'][quad]
return self._ele.page.run_cdp('DOM.getBoxModel', nodeId=self._ele.ids.node_id)['model'][quad]
except Exception:
return None
def _get_page_coord(self, x, y): def _get_page_coord(self, x, y):
"""根据绝对坐标获取窗口坐标""" """根据绝对坐标获取窗口坐标"""
@ -1628,25 +1625,29 @@ class Click(object):
return True return True
if not by_js: if not by_js:
self._ele.page.scroll.to_see(self._ele) try:
if self._ele.states.is_in_viewport: self._ele.page.scroll.to_see(self._ele)
client_x, client_y = self._ele.locations.viewport_click_point if self._ele.states.is_in_viewport:
if client_x: client_x, client_y = self._ele.locations.viewport_click_point
loc_x, loc_y = self._ele.locations.click_point if client_x:
loc_x, loc_y = self._ele.locations.click_point
click = do_it(client_x, client_y, loc_x, loc_y)
if click:
self._ele.page.wait.load_start(wait_loading)
return True
timeout = timeout if timeout is not None else self._ele.page.timeout
end_time = perf_counter() + timeout
while click is False and perf_counter() < end_time:
click = do_it(client_x, client_y, loc_x, loc_y) click = do_it(client_x, client_y, loc_x, loc_y)
if click:
self._ele.page.wait.load_start(wait_loading)
return True
if click is not None: timeout = timeout if timeout is not None else self._ele.page.timeout
self._ele.page.wait.load_start(wait_loading) end_time = perf_counter() + timeout
return True while click is False and perf_counter() < end_time:
click = do_it(client_x, client_y, loc_x, loc_y)
if click is not None:
self._ele.page.wait.load_start(wait_loading)
return True
except NoRectError:
by_js = True
if by_js is not False: if by_js is not False:
self._ele.run_js('this.click();') self._ele.run_js('this.click();')

View File

@ -84,7 +84,7 @@ class ChromiumFrame(ChromiumBase):
self._reload() self._reload()
try: try:
self.run_cdp('DOM.describeNode', nodeId=self.ids.node_id) self.page.run_cdp('DOM.describeNode', nodeId=self.ids.node_id)
except Exception: except Exception:
self._reload() self._reload()
# sleep(2) # sleep(2)
@ -291,7 +291,7 @@ class ChromiumFrame(ChromiumBase):
:return: 运行的结果 :return: 运行的结果
""" """
self._check_ok() self._check_ok()
return self.doc_ele.run_js(script, as_expr=as_expr, *args) return self.doc_ele.run_js(script, *args, as_expr=as_expr)
def parent(self, level_or_loc=1): def parent(self, level_or_loc=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位 """返回上面某一级父元素,可指定层数或用查询语法定位
@ -531,7 +531,7 @@ class ChromiumFrameScroll(ChromiumPageScroll):
""" """
ele = loc_or_ele if isinstance(loc_or_ele, ChromiumElement) else self._driver.ele(loc_or_ele) ele = loc_or_ele if isinstance(loc_or_ele, ChromiumElement) else self._driver.ele(loc_or_ele)
try: try:
self._driver.page.run_cdp('DOM.scrollIntoViewIfNeeded', nodeId=ele.node_id) self._driver.page.run_cdp('DOM.scrollIntoViewIfNeeded', nodeId=ele.ids.node_id)
except Exception: except Exception:
ele.run_js("this.scrollIntoView();") ele.run_js("this.scrollIntoView();")

View File

@ -38,3 +38,7 @@ class NotElementFoundError(BaseError):
class JavaScriptError(BaseError): class JavaScriptError(BaseError):
_info = 'JavaScript运行错误。' _info = 'JavaScript运行错误。'
class NoRectError(BaseError):
_info = '该元素没有位置及大小。'