eric7/WebBrowser/Tools/WebHitTestResult.py

branch
eric7
changeset 8312
800c432b34c8
parent 8207
d359172d11be
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an object for testing certain aspects of a web page.
8 """
9
10 #
11 # This code was ported from QupZilla.
12 # Copyright (C) David Rosca <nowrep@gmail.com>
13 #
14
15 from PyQt5.QtCore import QPoint, QRect, QUrl
16
17
18 class WebHitTestResult:
19 """
20 Class implementing an object for testing certain aspects of a web page.
21 """
22 def __init__(self, page, pos):
23 """
24 Constructor
25
26 @param page reference to the web page
27 @type WebBrowserPage
28 @param pos position to be tested
29 @type QPoint
30 """
31 self.__isNull = True
32 self.__isContentEditable = False
33 self.__isContentSelected = False
34 self.__isMediaPaused = False
35 self.__isMediaMuted = False
36 self.__pos = QPoint(pos)
37 self.__baseUrl = QUrl()
38 self.__alternateText = ""
39 self.__boundingRect = QRect()
40 self.__imageUrl = QUrl()
41 self.__linkTitle = ""
42 self.__linkUrl = QUrl()
43 self.__mediaUrl = QUrl()
44 self.__tagName = ""
45 self.__viewportPos = page.mapToViewport(pos)
46
47 script = """
48 (function() {{
49 var e = document.elementFromPoint({0}, {1});
50 if (!e)
51 return;
52 function isMediaElement(e) {{
53 return e.tagName.toLowerCase() == 'audio' ||
54 e.tagName.toLowerCase() == 'video';
55 }}
56 function isEditableElement(e) {{
57 if (e.isContentEditable)
58 return true;
59 if (e.tagName.toLowerCase() == 'input' ||
60 e.tagName.toLowerCase() == 'textarea')
61 return e.getAttribute('readonly') != 'readonly';
62 return false;
63 }}
64 function isSelected(e) {{
65 var selection = window.getSelection();
66 if (selection.type != 'Range')
67 return false;
68 return window.getSelection().containsNode(e, true);
69 }}
70 function attributeStr(e, a) {{
71 return e.getAttribute(a) || '';
72 }}
73 var res = {{
74 baseUrl: document.baseURI,
75 alternateText: e.getAttribute('alt'),
76 boundingRect: '',
77 imageUrl: '',
78 contentEditable: isEditableElement(e),
79 contentSelected: isSelected(e),
80 linkTitle: '',
81 linkUrl: '',
82 mediaUrl: '',
83 mediaPaused: false,
84 mediaMuted: false,
85 tagName: e.tagName.toLowerCase()
86 }};
87 var r = e.getBoundingClientRect();
88 res.boundingRect = [r.top, r.left, r.width, r.height];
89 if (e.tagName.toLowerCase() == 'img')
90 res.imageUrl = attributeStr(e, 'src').trim();
91 if (e.tagName.toLowerCase() == 'a') {{
92 res.linkTitle = e.text;
93 res.linkUrl = attributeStr(e, 'href').trim();
94 }}
95 while (e) {{
96 if (res.linkTitle == '' && e.tagName.toLowerCase() == 'a')
97 res.linkTitle = e.text;
98 if (res.linkUrl == '' && e.tagName.toLowerCase() == 'a')
99 res.linkUrl = attributeStr(e, 'href').trim();
100 if (res.mediaUrl == '' && isMediaElement(e)) {{
101 res.mediaUrl = e.currentSrc;
102 res.mediaPaused = e.paused;
103 res.mediaMuted = e.muted;
104 }}
105 e = e.parentElement;
106 }}
107 return res;
108 }})()
109 """.format(self.__viewportPos.x(), self.__viewportPos.y())
110 self.__populate(page.url(), page.execJavaScript(script))
111
112 def updateWithContextMenuData(self, data):
113 """
114 Public method to update the hit test data with data from the context
115 menu event.
116
117 @param data context menu data
118 @type QWebEngineContextMenuData
119 """
120 from PyQt5.QtWebEngineWidgets import QWebEngineContextMenuData
121 if not data.isValid() or data.position() != self.__pos:
122 return
123
124 self.__linkTitle = data.linkText()
125 self.__linkUrl = data.linkUrl()
126 self.__isContentEditable = data.isContentEditable()
127 self.__isContentSelected = bool(data.selectedText())
128
129 if (
130 data.mediaType() ==
131 QWebEngineContextMenuData.MediaType.MediaTypeImage
132 ):
133 self.__imageUrl = data.mediaUrl()
134 elif data.mediaType() in [
135 QWebEngineContextMenuData.MediaType.MediaTypeAudio,
136 QWebEngineContextMenuData.MediaType.MediaTypeVideo
137 ]:
138 self.__mediaUrl = data.mediaUrl()
139
140 def baseUrl(self):
141 """
142 Public method to get the base URL of the page.
143
144 @return base URL
145 @rtype QUrl
146 """
147 return self.__baseUrl
148
149 def alternateText(self):
150 """
151 Public method to get the alternate text.
152
153 @return alternate text
154 @rtype str
155 """
156 return self.__alternateText
157
158 def boundingRect(self):
159 """
160 Public method to get the bounding rectangle.
161
162 @return bounding rectangle
163 @rtype QRect
164 """
165 return QRect(self.__boundingRect)
166
167 def imageUrl(self):
168 """
169 Public method to get the URL of an image.
170
171 @return image URL
172 @rtype QUrl
173 """
174 return self.__imageUrl
175
176 def isContentEditable(self):
177 """
178 Public method to check for editable content.
179
180 @return flag indicating editable content
181 @rtype bool
182 """
183 return self.__isContentEditable
184
185 def isContentSelected(self):
186 """
187 Public method to check for selected content.
188
189 @return flag indicating selected content
190 @rtype bool
191 """
192 return self.__isContentSelected
193
194 def isNull(self):
195 """
196 Public method to test, if the hit test is empty.
197
198 @return flag indicating an empty object
199 @rtype bool
200 """
201 return self.__isNull
202
203 def linkTitle(self):
204 """
205 Public method to get the title for a link element.
206
207 @return title for a link element
208 @rtype str
209 """
210 return self.__linkTitle
211
212 def linkUrl(self):
213 """
214 Public method to get the URL for a link element.
215
216 @return URL for a link element
217 @rtype QUrl
218 """
219 return self.__linkUrl
220
221 def mediaUrl(self):
222 """
223 Public method to get the URL for a media element.
224
225 @return URL for a media element
226 @rtype QUrl
227 """
228 return self.__mediaUrl
229
230 def mediaPaused(self):
231 """
232 Public method to check, if a media element is paused.
233
234 @return flag indicating a paused media element
235 @rtype bool
236 """
237 return self.__isMediaPaused
238
239 def mediaMuted(self):
240 """
241 Public method to check, if a media element is muted.
242
243 @return flag indicating a muted media element
244 @rtype bool
245 """
246 return self.__isMediaMuted
247
248 def pos(self):
249 """
250 Public method to get the position of the hit test.
251
252 @return position of hit test
253 @rtype QPoint
254 """
255 return QPoint(self.__pos)
256
257 def viewportPos(self):
258 """
259 Public method to get the viewport position.
260
261 @return viewport position
262 @rtype QPoint
263 """
264 return QPoint(self.__viewportPos)
265
266 def tagName(self):
267 """
268 Public method to get the name of the tested tag.
269
270 @return name of the tested tag
271 @rtype str
272 """
273 return self.__tagName
274
275 def __populate(self, url, res):
276 """
277 Private method to populate the object.
278
279 @param url URL of the tested page
280 @type QUrl
281 @param res dictionary with result data from JavaScript
282 @type dict
283 """
284 if not res:
285 return
286
287 self.__baseUrl = QUrl(res["baseUrl"])
288 self.__alternateText = res["alternateText"]
289 self.__imageUrl = QUrl(res["imageUrl"])
290 self.__isContentEditable = res["contentEditable"]
291 self.__isContentSelected = res["contentSelected"]
292 self.__linkTitle = res["linkTitle"]
293 self.__linkUrl = QUrl(res["linkUrl"])
294 self.__mediaUrl = QUrl(res["mediaUrl"])
295 self.__isMediaPaused = res["mediaPaused"]
296 self.__isMediaMuted = res["mediaMuted"]
297 self.__tagName = res["tagName"]
298
299 rect = res["boundingRect"]
300 if len(rect) == 4:
301 self.__boundingRect = QRect(int(rect[0]), int(rect[1]),
302 int(rect[2]), int(rect[3]))
303
304 if not self.__imageUrl.isEmpty():
305 self.__imageUrl = url.resolved(self.__imageUrl)
306 if not self.__linkUrl.isEmpty():
307 self.__linkUrl = self.__baseUrl.resolved(self.__linkUrl)
308 if not self.__mediaUrl.isEmpty():
309 self.__mediaUrl = url.resolved(self.__mediaUrl)

eric ide

mercurial