|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 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 class WebHitTestResult(object): |
|
20 """ |
|
21 Class implementing an object for testing certain aspects of a web page. |
|
22 """ |
|
23 def __init__(self, page, pos): |
|
24 """ |
|
25 Constructor |
|
26 |
|
27 @param page reference to the web page |
|
28 @type WebBrowserPage |
|
29 @param pos position to be tested |
|
30 @type QPoint |
|
31 """ |
|
32 self.__isNull = True |
|
33 self.__isContentEditable = False |
|
34 self.__isContentSelected = False |
|
35 self.__isMediaPaused = False |
|
36 self.__isMediaMuted = False |
|
37 self.__pos = QPoint(pos) |
|
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 |
|
46 script = """ |
|
47 (function() {{ |
|
48 var e = document.elementFromPoint({0}, {1}); |
|
49 if (!e) |
|
50 return; |
|
51 function isMediaElement(e) {{ |
|
52 return e.tagName == 'AUDIO' || e.tagName == 'VIDEO'; |
|
53 }} |
|
54 function isEditableElement(e) {{ |
|
55 if (e.isContentEditable) |
|
56 return true; |
|
57 if (e.tagName == 'INPUT' || e.tagName == 'TEXTAREA') |
|
58 return e.getAttribute('readonly') != 'readonly'; |
|
59 return false; |
|
60 }} |
|
61 function isSelected(e) {{ |
|
62 var selection = window.getSelection(); |
|
63 if (selection.type != 'Range') |
|
64 return false; |
|
65 return window.getSelection().containsNode(e, true); |
|
66 }} |
|
67 var res = {{ |
|
68 alternateText: e.getAttribute('alt'), |
|
69 boundingRect: '', |
|
70 imageUrl: '', |
|
71 contentEditable: isEditableElement(e), |
|
72 contentSelected: isSelected(e), |
|
73 linkTitle: '', |
|
74 linkUrl: '', |
|
75 mediaUrl: '', |
|
76 mediaPaused: false, |
|
77 mediaMuted: false, |
|
78 tagName: e.tagName.toLowerCase() |
|
79 }}; |
|
80 var r = e.getBoundingClientRect(); |
|
81 res.boundingRect = [r.top, r.left, r.width, r.height]; |
|
82 if (e.tagName == 'IMG') |
|
83 res.imageUrl = e.getAttribute('src'); |
|
84 if (e.tagName == 'A') {{ |
|
85 res.linkTitle = e.text; |
|
86 res.linkUrl = e.getAttribute('href'); |
|
87 }} |
|
88 while (e) {{ |
|
89 if (res.linkTitle == '' && e.tagName == 'A') |
|
90 res.linkTitle = e.text; |
|
91 if (res.linkUrl == '' && e.tagName == 'A') |
|
92 res.linkUrl = e.getAttribute('href'); |
|
93 if (res.mediaUrl == '' && isMediaElement(e)) {{ |
|
94 res.mediaUrl = e.currentSrc; |
|
95 res.mediaPaused = e.paused; |
|
96 res.mediaMuted = e.muted; |
|
97 }} |
|
98 e = e.parentElement; |
|
99 }} |
|
100 return res; |
|
101 }})() |
|
102 """.format(pos.x(), pos.y()) |
|
103 self.__populate(page.url(), page.execJavaScript(script)) |
|
104 |
|
105 def alternateText(self): |
|
106 """ |
|
107 Public method to get the alternate text. |
|
108 |
|
109 @return alternate text |
|
110 @rtype str |
|
111 """ |
|
112 return self.__alternateText |
|
113 |
|
114 def boundingRect(self): |
|
115 """ |
|
116 Public method to get the bounding rectangle. |
|
117 |
|
118 @return bounding rectangle |
|
119 @rtype QRect |
|
120 """ |
|
121 return QRect(self.__boundingRect) |
|
122 |
|
123 def imageUrl(self): |
|
124 """ |
|
125 Public method to get the URL of an image. |
|
126 |
|
127 @return image URL |
|
128 @rtype QUrl |
|
129 """ |
|
130 return self.__imageUrl |
|
131 |
|
132 def isContentEditable(self): |
|
133 """ |
|
134 Public method to check for editable content. |
|
135 |
|
136 @return flag indicating editable content |
|
137 @rtype bool |
|
138 """ |
|
139 return self.__isContentEditable |
|
140 |
|
141 def isContentSelected(self): |
|
142 """ |
|
143 Public method to check for selected content. |
|
144 |
|
145 @return flag indicating selected content |
|
146 @rtype bool |
|
147 """ |
|
148 return self.__isContentSelected |
|
149 |
|
150 def isNull(self): |
|
151 """ |
|
152 Public method to test, if the hit test is empty. |
|
153 |
|
154 @return flag indicating an empty object |
|
155 @rtype bool |
|
156 """ |
|
157 return self.__isNull |
|
158 |
|
159 def linkTitle(self): |
|
160 """ |
|
161 Public method to get the title for a link element. |
|
162 |
|
163 @return title for a link element |
|
164 @rtype str |
|
165 """ |
|
166 return self.__linkTitle |
|
167 |
|
168 def linkUrl(self): |
|
169 """ |
|
170 Public method to get the URL for a link element. |
|
171 |
|
172 @return URL for a link element |
|
173 @rtype QUrl |
|
174 """ |
|
175 return self.__linkUrl |
|
176 |
|
177 def mediaUrl(self): |
|
178 """ |
|
179 Public method to get the URL for a media element. |
|
180 |
|
181 @return URL for a media element |
|
182 @rtype QUrl |
|
183 """ |
|
184 return self.__mediaUrl |
|
185 |
|
186 def mediaPaused(self): |
|
187 """ |
|
188 Public method to check, if a media element is paused. |
|
189 |
|
190 @return flag indicating a paused media element |
|
191 @rtype bool |
|
192 """ |
|
193 return self.__isMediaPaused |
|
194 |
|
195 def mediaMuted(self): |
|
196 """ |
|
197 Public method to check, if a media element is muted. |
|
198 |
|
199 @return flag indicating a muted media element |
|
200 @rtype bool |
|
201 """ |
|
202 return self.__isMediaMuted |
|
203 |
|
204 def pos(self): |
|
205 """ |
|
206 Public method to get the position of the hit test. |
|
207 |
|
208 @return position of hit test |
|
209 @rtype QPoint |
|
210 """ |
|
211 return QPoint(self.__pos) |
|
212 |
|
213 def tagName(self): |
|
214 """ |
|
215 Public method to get the name of the tested tag. |
|
216 |
|
217 @return name of the tested tag |
|
218 @rtype str |
|
219 """ |
|
220 return self.__tagName |
|
221 |
|
222 def __populate(self, url, res): |
|
223 """ |
|
224 Private method to populate the object. |
|
225 |
|
226 @param url URL of the tested page |
|
227 @type QUrl |
|
228 @param res dictionary with result data from JavaScript |
|
229 @type dict |
|
230 """ |
|
231 if not res: |
|
232 return |
|
233 |
|
234 self.__alternateText = res["alternateText"] |
|
235 self.__imageUrl = QUrl(res["imageUrl"]) |
|
236 self.__isContentEditable = res["contentEditable"] |
|
237 self.__isContentSelected = res["contentSelected"] |
|
238 self.__linkTitle = res["linkTitle"] |
|
239 self.__linkUrl = QUrl(res["linkUrl"]) |
|
240 self.__mediaUrl = QUrl(res["mediaUrl"]) |
|
241 self.__isMediaPaused = res["mediaPaused"] |
|
242 self.__isMediaMuted = res["mediaMuted"] |
|
243 self.__tagName = res["tagName"] |
|
244 |
|
245 rect = res["boundingRect"] |
|
246 if len(rect) == 4: |
|
247 self.__boundingRect = QRect(int(rect[0]), int(rect[1]), |
|
248 int(rect[2]), int(rect[3])) |
|
249 |
|
250 if not self.__imageUrl.isEmpty(): |
|
251 self.__imageUrl = url.resolved(self.__imageUrl) |
|
252 if not self.__linkUrl.isEmpty(): |
|
253 self.__linkUrl = url.resolved(self.__linkUrl) |
|
254 if not self.__mediaUrl.isEmpty(): |
|
255 self.__mediaUrl = url.resolved(self.__mediaUrl) |