eric6/WebBrowser/Tools/WebHitTestResult.py

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

eric ide

mercurial