eric6/WebBrowser/WebBrowserView.py

branch
maintenance
changeset 6989
8b8cadf8d7e9
parent 6923
d062df8f1d9f
parent 6942
2602857055c5
child 7286
7eb04391adf7
equal deleted inserted replaced
6938:7926553b7509 6989:8b8cadf8d7e9
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6
7 """
8 Module implementing the web browser using QWebEngineView.
9 """
10
11 from __future__ import unicode_literals
12 try:
13 str = unicode # __IGNORE_EXCEPTION__
14 except NameError:
15 pass
16
17 import os
18
19 from PyQt5.QtCore import pyqtSignal, pyqtSlot, PYQT_VERSION, Qt, QUrl, \
20 QFileInfo, QTimer, QEvent, QPoint, QPointF, QDateTime, QStandardPaths, \
21 QByteArray, QIODevice, QDataStream
22 from PyQt5.QtGui import QDesktopServices, QClipboard, QIcon, \
23 QContextMenuEvent, QPixmap, QCursor
24 from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication, QDialog
25 from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, \
26 QWebEngineDownloadItem
27
28 from E5Gui import E5MessageBox, E5FileDialog
29
30 from WebBrowser.WebBrowserWindow import WebBrowserWindow
31 from .WebBrowserPage import WebBrowserPage
32
33 from .Tools.WebIconLoader import WebIconLoader
34 from .Tools import Scripts
35
36 from . import WebInspector
37 from .Tools.WebBrowserTools import readAllFileContents, pixmapToDataUrl
38
39 import Preferences
40 import UI.PixmapCache
41 import Utilities
42 from Globals import qVersionTuple
43
44
45 class WebBrowserView(QWebEngineView):
46 """
47 Class implementing the web browser view widget.
48
49 @signal sourceChanged(QUrl) emitted after the current URL has changed
50 @signal forwardAvailable(bool) emitted after the current URL has changed
51 @signal backwardAvailable(bool) emitted after the current URL has changed
52 @signal highlighted(str) emitted, when the mouse hovers over a link
53 @signal search(QUrl) emitted, when a search is requested
54 @signal zoomValueChanged(int) emitted to signal a change of the zoom value
55 @signal faviconChanged() emitted to signal a changed web site icon
56 @signal safeBrowsingAbort() emitted to indicate an abort due to a safe
57 browsing event
58 @signal safeBrowsingBad(threatType, threatMessages) emitted to indicate a
59 malicious web site as determined by safe browsing
60 """
61 sourceChanged = pyqtSignal(QUrl)
62 forwardAvailable = pyqtSignal(bool)
63 backwardAvailable = pyqtSignal(bool)
64 highlighted = pyqtSignal(str)
65 search = pyqtSignal(QUrl)
66 zoomValueChanged = pyqtSignal(int)
67 faviconChanged = pyqtSignal()
68 safeBrowsingAbort = pyqtSignal()
69 safeBrowsingBad = pyqtSignal(str, str)
70
71 ZoomLevels = [
72 30, 40, 50, 67, 80, 90,
73 100,
74 110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300,
75 ]
76 ZoomLevelDefault = 100
77
78 def __init__(self, mainWindow, parent=None, name=""):
79 """
80 Constructor
81
82 @param mainWindow reference to the main window (WebBrowserWindow)
83 @param parent parent widget of this window (QWidget)
84 @param name name of this window (string)
85 """
86 super(WebBrowserView, self).__init__(parent)
87 self.setObjectName(name)
88
89 self.__rwhvqt = None
90 self.installEventFilter(self)
91
92 self.__speedDial = WebBrowserWindow.speedDial()
93
94 self.__page = None
95 self.__createNewPage()
96
97 self.__mw = mainWindow
98 self.__tabWidget = parent
99 self.__isLoading = False
100 self.__progress = 0
101 self.__siteIconLoader = None
102 self.__siteIcon = QIcon()
103 self.__menu = QMenu(self)
104 self.__clickedPos = QPoint()
105 self.__firstLoad = False
106 self.__preview = QPixmap()
107
108 self.__currentZoom = 100
109 self.__zoomLevels = WebBrowserView.ZoomLevels[:]
110
111 self.iconUrlChanged.connect(self.__iconUrlChanged)
112 self.urlChanged.connect(self.__urlChanged)
113 self.page().linkHovered.connect(self.__linkHovered)
114
115 self.loadStarted.connect(self.__loadStarted)
116 self.loadProgress.connect(self.__loadProgress)
117 self.loadFinished.connect(self.__loadFinished)
118 self.renderProcessTerminated.connect(self.__renderProcessTerminated)
119
120 self.__mw.openSearchManager().currentEngineChanged.connect(
121 self.__currentEngineChanged)
122
123 self.setAcceptDrops(True)
124
125 self.__rss = []
126
127 self.__clickedFrame = None
128
129 self.__mw.personalInformationManager().connectPage(self.page())
130
131 self.__inspector = None
132 WebInspector.registerView(self)
133
134 self.__restoreData = None
135
136 if qVersionTuple() >= (5, 8, 0):
137 if self.parentWidget() is not None:
138 self.parentWidget().installEventFilter(self)
139
140 if qVersionTuple() >= (5, 8, 0) and qVersionTuple() < (5, 11, 0):
141 lay = self.layout()
142 lay.currentChanged.connect(
143 lambda: QTimer.singleShot(0, self.__setRwhvqt))
144 self.__setRwhvqt()
145
146 self.grabGesture(Qt.PinchGesture)
147
148 def __createNewPage(self):
149 """
150 Private method to create a new page object.
151 """
152 self.__page = WebBrowserPage(self)
153 self.setPage(self.__page)
154
155 self.__page.safeBrowsingAbort.connect(self.safeBrowsingAbort)
156 self.__page.safeBrowsingBad.connect(self.safeBrowsingBad)
157 self.__page.printPageRequested.connect(self.__printPage)
158 try:
159 self.__page.quotaRequested.connect(self.__quotaRequested)
160 # The registerProtocolHandlerRequested signal is handled in
161 # WebBrowserPage.
162 except AttributeError:
163 # pre Qt 5.11
164 pass
165 try:
166 self.__page.selectClientCertificate.connect(
167 self.__selectClientCertificate)
168 except AttributeError:
169 # pre Qt 5.12
170 pass
171
172 def __setRwhvqt(self):
173 """
174 Private slot to set widget that receives input events.
175 """
176 self.grabGesture(Qt.PinchGesture)
177 self.__rwhvqt = self.focusProxy()
178 if self.__rwhvqt:
179 self.__rwhvqt.grabGesture(Qt.PinchGesture)
180 self.__rwhvqt.installEventFilter(self)
181 else:
182 print("Focus proxy is null!") # __IGNORE_WARNING_M801__
183
184 def __currentEngineChanged(self):
185 """
186 Private slot to track a change of the current search engine.
187 """
188 if self.url().toString() == "eric:home":
189 self.reload()
190
191 def mainWindow(self):
192 """
193 Public method to get a reference to the main window.
194
195 @return reference to the main window
196 @rtype WebBrowserWindow
197 """
198 return self.__mw
199
200 def tabWidget(self):
201 """
202 Public method to get a reference to the tab widget containing this
203 view.
204
205 @return reference to the tab widget
206 @rtype WebBrowserTabWidget
207 """
208 return self.__tabWidget
209
210 def load(self, url):
211 """
212 Public method to load a web site.
213
214 @param url URL to be loaded
215 @type QUrl
216 """
217 if self.__page is not None and \
218 not self.__page.acceptNavigationRequest(
219 url, QWebEnginePage.NavigationTypeTyped, True):
220 return
221
222 super(WebBrowserView, self).load(url)
223
224 if not self.__firstLoad:
225 self.__firstLoad = True
226 WebInspector.pushView(self)
227
228 def setSource(self, name, newTab=False):
229 """
230 Public method used to set the source to be displayed.
231
232 @param name filename to be shown (QUrl)
233 @param newTab flag indicating to open the URL in a new tab (bool)
234 """
235 if name is None or not name.isValid():
236 return
237
238 if newTab:
239 # open in a new tab
240 self.__mw.newTab(name)
241 return
242
243 if not name.scheme():
244 if not os.path.exists(name.toString()):
245 name.setScheme(Preferences.getWebBrowser("DefaultScheme"))
246 else:
247 if Utilities.isWindowsPlatform():
248 name.setUrl("file:///" + Utilities.fromNativeSeparators(
249 name.toString()))
250 else:
251 name.setUrl("file://" + name.toString())
252
253 if len(name.scheme()) == 1 or \
254 name.scheme() == "file":
255 # name is a local file
256 if name.scheme() and len(name.scheme()) == 1:
257 # it is a local path on win os
258 name = QUrl.fromLocalFile(name.toString())
259
260 if not QFileInfo(name.toLocalFile()).exists():
261 E5MessageBox.critical(
262 self,
263 self.tr("eric6 Web Browser"),
264 self.tr(
265 """<p>The file <b>{0}</b> does not exist.</p>""")
266 .format(name.toLocalFile()))
267 return
268
269 if name.toLocalFile().lower().endswith((".pdf", ".chm")):
270 started = QDesktopServices.openUrl(name)
271 if not started:
272 E5MessageBox.critical(
273 self,
274 self.tr("eric6 Web Browser"),
275 self.tr(
276 """<p>Could not start a viewer"""
277 """ for file <b>{0}</b>.</p>""")
278 .format(name.path()))
279 return
280 elif name.scheme() in ["mailto"]:
281 started = QDesktopServices.openUrl(name)
282 if not started:
283 E5MessageBox.critical(
284 self,
285 self.tr("eric6 Web Browser"),
286 self.tr(
287 """<p>Could not start an application"""
288 """ for URL <b>{0}</b>.</p>""")
289 .format(name.toString()))
290 return
291 else:
292 if name.toString().lower().endswith((".pdf", ".chm")):
293 started = QDesktopServices.openUrl(name)
294 if not started:
295 E5MessageBox.critical(
296 self,
297 self.tr("eric6 Web Browser"),
298 self.tr(
299 """<p>Could not start a viewer"""
300 """ for file <b>{0}</b>.</p>""")
301 .format(name.path()))
302 return
303
304 self.load(name)
305
306 def source(self):
307 """
308 Public method to return the URL of the loaded page.
309
310 @return URL loaded in the help browser (QUrl)
311 """
312 return self.url()
313
314 def documentTitle(self):
315 """
316 Public method to return the title of the loaded page.
317
318 @return title (string)
319 """
320 return self.title()
321
322 def backward(self):
323 """
324 Public slot to move backwards in history.
325 """
326 self.triggerPageAction(QWebEnginePage.Back)
327 self.__urlChanged(self.history().currentItem().url())
328
329 def forward(self):
330 """
331 Public slot to move forward in history.
332 """
333 self.triggerPageAction(QWebEnginePage.Forward)
334 self.__urlChanged(self.history().currentItem().url())
335
336 def home(self):
337 """
338 Public slot to move to the first page loaded.
339 """
340 homeUrl = QUrl(Preferences.getWebBrowser("HomePage"))
341 self.setSource(homeUrl)
342 self.__urlChanged(self.history().currentItem().url())
343
344 def reload(self):
345 """
346 Public slot to reload the current page.
347 """
348 self.triggerPageAction(QWebEnginePage.Reload)
349
350 def reloadBypassingCache(self):
351 """
352 Public slot to reload the current page bypassing the cache.
353 """
354 self.triggerPageAction(QWebEnginePage.ReloadAndBypassCache)
355
356 def copy(self):
357 """
358 Public slot to copy the selected text.
359 """
360 self.triggerPageAction(QWebEnginePage.Copy)
361
362 def cut(self):
363 """
364 Public slot to cut the selected text.
365 """
366 self.triggerPageAction(QWebEnginePage.Cut)
367
368 def paste(self):
369 """
370 Public slot to paste text from the clipboard.
371 """
372 self.triggerPageAction(QWebEnginePage.Paste)
373
374 def undo(self):
375 """
376 Public slot to undo the last edit action.
377 """
378 self.triggerPageAction(QWebEnginePage.Undo)
379
380 def redo(self):
381 """
382 Public slot to redo the last edit action.
383 """
384 self.triggerPageAction(QWebEnginePage.Redo)
385
386 def selectAll(self):
387 """
388 Public slot to select all text.
389 """
390 self.triggerPageAction(QWebEnginePage.SelectAll)
391
392 def unselect(self):
393 """
394 Public slot to clear the current selection.
395 """
396 try:
397 self.triggerPageAction(QWebEnginePage.Unselect)
398 except AttributeError:
399 # prior to 5.7.0
400 self.page().runJavaScript(
401 "window.getSelection().empty()",
402 WebBrowserPage.SafeJsWorld)
403
404 def isForwardAvailable(self):
405 """
406 Public method to determine, if a forward move in history is possible.
407
408 @return flag indicating move forward is possible (boolean)
409 """
410 return self.history().canGoForward()
411
412 def isBackwardAvailable(self):
413 """
414 Public method to determine, if a backwards move in history is possible.
415
416 @return flag indicating move backwards is possible (boolean)
417 """
418 return self.history().canGoBack()
419
420 def __levelForZoom(self, zoom):
421 """
422 Private method determining the zoom level index given a zoom factor.
423
424 @param zoom zoom factor (integer)
425 @return index of zoom factor (integer)
426 """
427 try:
428 index = self.__zoomLevels.index(zoom)
429 except ValueError:
430 for index in range(len(self.__zoomLevels)):
431 if zoom <= self.__zoomLevels[index]:
432 break
433 return index
434
435 def setZoomValue(self, value, saveValue=True):
436 """
437 Public method to set the zoom value.
438
439 @param value zoom value (integer)
440 @keyparam saveValue flag indicating to save the zoom value with the
441 zoom manager
442 @type bool
443 """
444 if value != self.__currentZoom:
445 self.setZoomFactor(value / 100.0)
446 self.__currentZoom = value
447 if saveValue and not self.__mw.isPrivate():
448 from .ZoomManager import ZoomManager
449 ZoomManager.instance().setZoomValue(self.url(), value)
450 self.zoomValueChanged.emit(value)
451
452 def zoomValue(self):
453 """
454 Public method to get the current zoom value.
455
456 @return zoom value (integer)
457 """
458 val = self.zoomFactor() * 100
459 return int(val)
460
461 def zoomIn(self):
462 """
463 Public slot to zoom into the page.
464 """
465 index = self.__levelForZoom(self.__currentZoom)
466 if index < len(self.__zoomLevels) - 1:
467 self.setZoomValue(self.__zoomLevels[index + 1])
468
469 def zoomOut(self):
470 """
471 Public slot to zoom out of the page.
472 """
473 index = self.__levelForZoom(self.__currentZoom)
474 if index > 0:
475 self.setZoomValue(self.__zoomLevels[index - 1])
476
477 def zoomReset(self):
478 """
479 Public method to reset the zoom factor.
480 """
481 index = self.__levelForZoom(WebBrowserView.ZoomLevelDefault)
482 self.setZoomValue(self.__zoomLevels[index])
483
484 def mapToViewport(self, pos):
485 """
486 Public method to map a position to the viewport.
487
488 @param pos position to be mapped
489 @type QPoint
490 @return viewport position
491 @rtype QPoint
492 """
493 return self.page().mapToViewport(pos)
494
495 def hasSelection(self):
496 """
497 Public method to determine, if there is some text selected.
498
499 @return flag indicating text has been selected (boolean)
500 """
501 return self.selectedText() != ""
502
503 def findNextPrev(self, txt, case, backwards, callback):
504 """
505 Public slot to find the next occurrence of a text.
506
507 @param txt text to search for (string)
508 @param case flag indicating a case sensitive search (boolean)
509 @param backwards flag indicating a backwards search (boolean)
510 @param callback reference to a function with a bool parameter
511 @type function(bool) or None
512 """
513 findFlags = QWebEnginePage.FindFlags()
514 if case:
515 findFlags |= QWebEnginePage.FindCaseSensitively
516 if backwards:
517 findFlags |= QWebEnginePage.FindBackward
518
519 if callback is None:
520 self.findText(txt, findFlags)
521 else:
522 self.findText(txt, findFlags, callback)
523
524 def contextMenuEvent(self, evt):
525 """
526 Protected method called to create a context menu.
527
528 This method is overridden from QWebEngineView.
529
530 @param evt reference to the context menu event object
531 (QContextMenuEvent)
532 """
533 pos = evt.pos()
534 reason = evt.reason()
535 QTimer.singleShot(
536 0,
537 lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)))
538 # needs to be done this way because contextMenuEvent is blocking
539 # the main loop
540
541 def _contextMenuEvent(self, evt):
542 """
543 Protected method called to create a context menu.
544
545 This method is overridden from QWebEngineView.
546
547 @param evt reference to the context menu event object
548 (QContextMenuEvent)
549 """
550 self.__menu.clear()
551
552 hitTest = self.page().hitTestContent(evt.pos())
553
554 self.__createContextMenu(self.__menu, hitTest)
555
556 if not hitTest.isContentEditable() and not hitTest.isContentSelected():
557 self.__menu.addSeparator()
558 self.__menu.addAction(self.__mw.adBlockIcon().menuAction())
559
560 if qVersionTuple() >= (5, 11, 0) or \
561 Preferences.getWebBrowser("WebInspectorEnabled"):
562 self.__menu.addSeparator()
563 self.__menu.addAction(
564 UI.PixmapCache.getIcon("webInspector.png"),
565 self.tr("Inspect Element..."), self.__webInspector)
566
567 if not self.__menu.isEmpty():
568 pos = evt.globalPos()
569 self.__menu.popup(QPoint(pos.x(), pos.y() + 1))
570
571 def __createContextMenu(self, menu, hitTest):
572 """
573 Private method to populate the context menu.
574
575 @param menu reference to the menu to be populated
576 @type QMenu
577 @param hitTest reference to the hit test object
578 @type WebHitTestResult
579 """
580 spellCheckActionCount = 0
581 if qVersionTuple() >= (5, 7, 0) and PYQT_VERSION >= 0x50700:
582 contextMenuData = self.page().contextMenuData()
583 hitTest.updateWithContextMenuData(contextMenuData)
584
585 if qVersionTuple() >= (5, 8, 0) and PYQT_VERSION >= 0x50800 and \
586 bool(contextMenuData.misspelledWord()):
587 boldFont = menu.font()
588 boldFont.setBold(True)
589
590 for suggestion in contextMenuData.spellCheckerSuggestions():
591 act = menu.addAction(suggestion)
592 act.setFont(boldFont)
593 act.triggered.connect(
594 lambda: self.__replaceMisspelledWord)(act)
595
596 if not bool(menu.actions()):
597 menu.addAction(self.tr("No suggestions")).setEnabled(False)
598
599 menu.addSeparator()
600 spellCheckActionCount = len(menu.actions())
601
602 if not hitTest.linkUrl().isEmpty() and \
603 hitTest.linkUrl().scheme() != "javascript":
604 self.__createLinkContextMenu(menu, hitTest)
605
606 if not hitTest.imageUrl().isEmpty():
607 self.__createImageContextMenu(menu, hitTest)
608
609 if not hitTest.mediaUrl().isEmpty():
610 self.__createMediaContextMenu(menu, hitTest)
611
612 if hitTest.isContentEditable():
613 # check, if only spell checker actions were added
614 if len(menu.actions()) == spellCheckActionCount:
615 menu.addAction(self.__mw.undoAct)
616 menu.addAction(self.__mw.redoAct)
617 menu.addSeparator()
618 menu.addAction(self.__mw.cutAct)
619 menu.addAction(self.__mw.copyAct)
620 menu.addAction(self.__mw.pasteAct)
621 menu.addSeparator()
622 self.__mw.personalInformationManager().createSubMenu(
623 menu, self, hitTest)
624
625 if hitTest.tagName() == "input":
626 menu.addSeparator()
627 act = menu.addAction("")
628 act.setVisible(False)
629 self.__checkForForm(act, hitTest.pos())
630
631 if self.selectedText():
632 self.__createSelectedTextContextMenu(menu, hitTest)
633
634 if self.__menu.isEmpty():
635 self.__createPageContextMenu(menu)
636
637 def __createLinkContextMenu(self, menu, hitTest):
638 """
639 Private method to populate the context menu for URLs.
640
641 @param menu reference to the menu to be populated
642 @type QMenu
643 @param hitTest reference to the hit test object
644 @type WebHitTestResult
645 """
646 if not menu.isEmpty():
647 menu.addSeparator()
648
649 act = menu.addAction(
650 UI.PixmapCache.getIcon("openNewTab.png"),
651 self.tr("Open Link in New Tab\tCtrl+LMB"))
652 act.setData(hitTest.linkUrl())
653 act.triggered.connect(
654 lambda: self.__openLinkInNewTab(act))
655 act = menu.addAction(
656 UI.PixmapCache.getIcon("newWindow.png"),
657 self.tr("Open Link in New Window"))
658 act.setData(hitTest.linkUrl())
659 act.triggered.connect(
660 lambda: self.__openLinkInNewWindow(act))
661 act = menu.addAction(
662 UI.PixmapCache.getIcon("privateMode.png"),
663 self.tr("Open Link in New Private Window"))
664 act.setData(hitTest.linkUrl())
665 act.triggered.connect(
666 lambda: self.__openLinkInNewPrivateWindow(act))
667 menu.addSeparator()
668 menu.addAction(
669 UI.PixmapCache.getIcon("download.png"),
670 self.tr("Save Lin&k"), self.__downloadLink)
671 act = menu.addAction(
672 UI.PixmapCache.getIcon("bookmark22.png"),
673 self.tr("Bookmark this Link"))
674 act.setData(hitTest.linkUrl())
675 act.triggered.connect(
676 lambda: self.__bookmarkLink(act))
677 menu.addSeparator()
678 act = menu.addAction(
679 UI.PixmapCache.getIcon("editCopy.png"),
680 self.tr("Copy Link to Clipboard"))
681 act.setData(hitTest.linkUrl())
682 act.triggered.connect(
683 lambda: self.__copyLink(act))
684 act = menu.addAction(
685 UI.PixmapCache.getIcon("mailSend.png"),
686 self.tr("Send Link"))
687 act.setData(hitTest.linkUrl())
688 act.triggered.connect(
689 lambda: self.__sendLink(act))
690 if Preferences.getWebBrowser("VirusTotalEnabled") and \
691 Preferences.getWebBrowser("VirusTotalServiceKey") != "":
692 act = menu.addAction(
693 UI.PixmapCache.getIcon("virustotal.png"),
694 self.tr("Scan Link with VirusTotal"))
695 act.setData(hitTest.linkUrl())
696 act.triggered.connect(
697 lambda: self.__virusTotal(act))
698
699 def __createImageContextMenu(self, menu, hitTest):
700 """
701 Private method to populate the context menu for images.
702
703 @param menu reference to the menu to be populated
704 @type QMenu
705 @param hitTest reference to the hit test object
706 @type WebHitTestResult
707 """
708 if not menu.isEmpty():
709 menu.addSeparator()
710
711 act = menu.addAction(
712 UI.PixmapCache.getIcon("openNewTab.png"),
713 self.tr("Open Image in New Tab"))
714 act.setData(hitTest.imageUrl())
715 act.triggered.connect(
716 lambda: self.__openLinkInNewTab(act))
717 menu.addSeparator()
718 menu.addAction(
719 UI.PixmapCache.getIcon("download.png"),
720 self.tr("Save Image"), self.__downloadImage)
721 menu.addAction(
722 self.tr("Copy Image to Clipboard"), self.__copyImage)
723 act = menu.addAction(
724 UI.PixmapCache.getIcon("editCopy.png"),
725 self.tr("Copy Image Location to Clipboard"))
726 act.setData(hitTest.imageUrl())
727 act.triggered.connect(
728 lambda: self.__copyLink(act))
729 act = menu.addAction(
730 UI.PixmapCache.getIcon("mailSend.png"),
731 self.tr("Send Image Link"))
732 act.setData(hitTest.imageUrl())
733 act.triggered.connect(
734 lambda: self.__sendLink(act))
735
736 if hitTest.imageUrl().scheme() in ["http", "https"]:
737 menu.addSeparator()
738 engine = WebBrowserWindow.imageSearchEngine()
739 searchEngineName = engine.searchEngine()
740 act = menu.addAction(
741 UI.PixmapCache.getIcon("{0}.png".format(
742 searchEngineName.lower())),
743 self.tr("Search image in {0}").format(searchEngineName))
744 act.setData(engine.getSearchQuery(hitTest.imageUrl()))
745 act.triggered.connect(
746 lambda: self.__searchImage(act))
747 self.__imageSearchMenu = menu.addMenu(
748 self.tr("Search image with..."))
749 for searchEngineName in engine.searchEngineNames():
750 act = self.__imageSearchMenu.addAction(
751 UI.PixmapCache.getIcon("{0}.png".format(
752 searchEngineName.lower())),
753 self.tr("Search image in {0}").format(searchEngineName))
754 act.setData(engine.getSearchQuery(
755 hitTest.imageUrl(), searchEngineName))
756 act.triggered.connect(
757 lambda: self.__searchImage(act))
758
759 menu.addSeparator()
760 act = menu.addAction(
761 UI.PixmapCache.getIcon("adBlockPlus.png"),
762 self.tr("Block Image"))
763 act.setData(hitTest.imageUrl().toString())
764 act.triggered.connect(
765 lambda: self.__blockImage(act))
766 if Preferences.getWebBrowser("VirusTotalEnabled") and \
767 Preferences.getWebBrowser("VirusTotalServiceKey") != "":
768 act = menu.addAction(
769 UI.PixmapCache.getIcon("virustotal.png"),
770 self.tr("Scan Image with VirusTotal"))
771 act.setData(hitTest.imageUrl())
772 act.triggered.connect(
773 lambda: self.__virusTotal(act))
774
775 def __createMediaContextMenu(self, menu, hitTest):
776 """
777 Private method to populate the context menu for media elements.
778
779 @param menu reference to the menu to be populated
780 @type QMenu
781 @param hitTest reference to the hit test object
782 @type WebHitTestResult
783 """
784 if not menu.isEmpty():
785 menu.addSeparator()
786
787 if hitTest.mediaPaused():
788 menu.addAction(
789 UI.PixmapCache.getIcon("mediaPlaybackStart.png"),
790 self.tr("Play"), self.__pauseMedia)
791 else:
792 menu.addAction(
793 UI.PixmapCache.getIcon("mediaPlaybackPause.png"),
794 self.tr("Pause"), self.__pauseMedia)
795 if hitTest.mediaMuted():
796 menu.addAction(
797 UI.PixmapCache.getIcon("audioVolumeHigh.png"),
798 self.tr("Unmute"), self.__muteMedia)
799 else:
800 menu.addAction(
801 UI.PixmapCache.getIcon("audioVolumeMuted.png"),
802 self.tr("Mute"), self.__muteMedia)
803 menu.addSeparator()
804 act = menu.addAction(
805 UI.PixmapCache.getIcon("editCopy.png"),
806 self.tr("Copy Media Address to Clipboard"))
807 act.setData(hitTest.mediaUrl())
808 act.triggered.connect(
809 lambda: self.__copyLink(act))
810 act = menu.addAction(
811 UI.PixmapCache.getIcon("mailSend.png"),
812 self.tr("Send Media Address"))
813 act.setData(hitTest.mediaUrl())
814 act.triggered.connect(
815 lambda: self.__sendLink(act))
816 menu.addAction(
817 UI.PixmapCache.getIcon("download.png"),
818 self.tr("Save Media"), self.__downloadMedia)
819
820 def __createSelectedTextContextMenu(self, menu, hitTest):
821 """
822 Private method to populate the context menu for selected text.
823
824 @param menu reference to the menu to be populated
825 @type QMenu
826 @param hitTest reference to the hit test object
827 @type WebHitTestResult
828 """
829 if not menu.isEmpty():
830 menu.addSeparator()
831
832 menu.addAction(self.__mw.copyAct)
833 menu.addSeparator()
834 act = menu.addAction(
835 UI.PixmapCache.getIcon("mailSend.png"),
836 self.tr("Send Text"))
837 act.setData(self.selectedText())
838 act.triggered.connect(
839 lambda: self.__sendLink(act))
840
841 engineName = self.__mw.openSearchManager().currentEngineName()
842 if engineName:
843 menu.addAction(self.tr("Search with '{0}'").format(engineName),
844 self.__searchDefaultRequested)
845
846 from .OpenSearch.OpenSearchEngineAction import \
847 OpenSearchEngineAction
848
849 self.__searchMenu = menu.addMenu(self.tr("Search with..."))
850 engineNames = self.__mw.openSearchManager().allEnginesNames()
851 for engineName in engineNames:
852 engine = self.__mw.openSearchManager().engine(engineName)
853 act = OpenSearchEngineAction(engine, self.__searchMenu)
854 act.setData(engineName)
855 self.__searchMenu.addAction(act)
856 self.__searchMenu.triggered.connect(self.__searchRequested)
857
858 menu.addSeparator()
859
860 from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
861 languages = Preferences.toList(
862 Preferences.Prefs.settings.value(
863 "WebBrowser/AcceptLanguages",
864 WebBrowserLanguagesDialog.defaultAcceptLanguages()))
865 if languages:
866 language = languages[0]
867 langCode = language.split("[")[1][:2]
868 googleTranslatorUrl = QUrl(
869 "http://translate.google.com/#auto/{0}/{1}".format(
870 langCode, self.selectedText()))
871 act = menu.addAction(
872 UI.PixmapCache.getIcon("translate.png"),
873 self.tr("Google Translate"))
874 act.setData(googleTranslatorUrl)
875 act.triggered.connect(
876 lambda: self.__openLinkInNewTab(act))
877 wiktionaryUrl = QUrl(
878 "http://{0}.wiktionary.org/wiki/Special:Search?search={1}"
879 .format(langCode, self.selectedText()))
880 act = menu.addAction(
881 UI.PixmapCache.getIcon("wikipedia.png"),
882 self.tr("Dictionary"))
883 act.setData(wiktionaryUrl)
884 act.triggered.connect(
885 lambda: self.__openLinkInNewTab(act))
886 menu.addSeparator()
887
888 guessedUrl = QUrl.fromUserInput(self.selectedText().strip())
889 if self.__isUrlValid(guessedUrl):
890 act = menu.addAction(self.tr("Go to web address"))
891 act.setData(guessedUrl)
892 act.triggered.connect(
893 lambda: self.__openLinkInNewTab(act))
894
895 def __createPageContextMenu(self, menu):
896 """
897 Private method to populate the basic context menu.
898
899 @param menu reference to the menu to be populated
900 @type QMenu
901 """
902 menu.addAction(self.__mw.newTabAct)
903 menu.addAction(self.__mw.newAct)
904 menu.addSeparator()
905 if self.__mw.saveAsAct is not None:
906 menu.addAction(self.__mw.saveAsAct)
907 menu.addAction(self.__mw.saveVisiblePageScreenAct)
908 menu.addSeparator()
909
910 if self.url().toString() == "eric:speeddial":
911 # special menu for the spedd dial page
912 menu.addAction(self.__mw.backAct)
913 menu.addAction(self.__mw.forwardAct)
914 menu.addSeparator()
915 menu.addAction(
916 UI.PixmapCache.getIcon("plus.png"),
917 self.tr("Add New Page"), self.__addSpeedDial)
918 menu.addAction(
919 UI.PixmapCache.getIcon("preferences-general.png"),
920 self.tr("Configure Speed Dial"), self.__configureSpeedDial)
921 menu.addSeparator()
922 menu.addAction(
923 UI.PixmapCache.getIcon("reload.png"),
924 self.tr("Reload All Dials"), self.__reloadAllSpeedDials)
925 menu.addSeparator()
926 menu.addAction(
927 self.tr("Reset to Default Dials"), self.__resetSpeedDials)
928 return
929
930 menu.addAction(
931 UI.PixmapCache.getIcon("bookmark22.png"),
932 self.tr("Bookmark this Page"), self.addBookmark)
933 act = menu.addAction(
934 UI.PixmapCache.getIcon("editCopy.png"),
935 self.tr("Copy Page Link"))
936 act.setData(self.url())
937 act.triggered.connect(
938 lambda: self.__copyLink(act))
939 act = menu.addAction(
940 UI.PixmapCache.getIcon("mailSend.png"),
941 self.tr("Send Page Link"))
942 act.setData(self.url())
943 act.triggered.connect(
944 lambda: self.__sendLink(act))
945 menu.addSeparator()
946
947 from .UserAgent.UserAgentMenu import UserAgentMenu
948 self.__userAgentMenu = UserAgentMenu(self.tr("User Agent"),
949 url=self.url())
950 menu.addMenu(self.__userAgentMenu)
951 menu.addSeparator()
952 menu.addAction(self.__mw.backAct)
953 menu.addAction(self.__mw.forwardAct)
954 menu.addAction(self.__mw.homeAct)
955 menu.addAction(self.__mw.reloadAct)
956 menu.addAction(self.__mw.stopAct)
957 menu.addSeparator()
958 menu.addAction(self.__mw.zoomInAct)
959 menu.addAction(self.__mw.zoomResetAct)
960 menu.addAction(self.__mw.zoomOutAct)
961 menu.addSeparator()
962 menu.addAction(self.__mw.selectAllAct)
963 menu.addSeparator()
964 menu.addAction(self.__mw.findAct)
965 menu.addSeparator()
966 menu.addAction(self.__mw.pageSourceAct)
967 menu.addSeparator()
968 menu.addAction(self.__mw.siteInfoAct)
969 if self.url().scheme() in ["http", "https"]:
970 menu.addSeparator()
971
972 w3url = QUrl.fromEncoded(
973 b"http://validator.w3.org/check?uri=" +
974 QUrl.toPercentEncoding(bytes(self.url().toEncoded()).decode()))
975 act = menu.addAction(
976 UI.PixmapCache.getIcon("w3.png"),
977 self.tr("Validate Page"))
978 act.setData(w3url)
979 act.triggered.connect(
980 lambda: self.__openLinkInNewTab(act))
981
982 from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
983 languages = Preferences.toList(
984 Preferences.Prefs.settings.value(
985 "WebBrowser/AcceptLanguages",
986 WebBrowserLanguagesDialog.defaultAcceptLanguages()))
987 if languages:
988 language = languages[0]
989 langCode = language.split("[")[1][:2]
990 googleTranslatorUrl = QUrl.fromEncoded(
991 b"http://translate.google.com/translate?sl=auto&tl=" +
992 langCode.encode() +
993 b"&u=" +
994 QUrl.toPercentEncoding(
995 bytes(self.url().toEncoded()).decode()))
996 act = menu.addAction(
997 UI.PixmapCache.getIcon("translate.png"),
998 self.tr("Google Translate"))
999 act.setData(googleTranslatorUrl)
1000 act.triggered.connect(
1001 lambda: self.__openLinkInNewTab(act))
1002
1003 def __checkForForm(self, act, pos):
1004 """
1005 Private method to check the given position for an open search form.
1006
1007 @param act reference to the action to be populated upon success
1008 @type QAction
1009 @param pos position to be tested
1010 @type QPoint
1011 """
1012 self.__clickedPos = self.mapToViewport(pos)
1013
1014 from .Tools import Scripts
1015 script = Scripts.getFormData(self.__clickedPos)
1016 self.page().runJavaScript(
1017 script,
1018 WebBrowserPage.SafeJsWorld,
1019 lambda res: self.__checkForFormCallback(res, act))
1020
1021 def __checkForFormCallback(self, res, act):
1022 """
1023 Private method handling the __checkForForm result.
1024
1025 @param res result dictionary generated by JavaScript
1026 @type dict
1027 @param act reference to the action to be populated upon success
1028 @type QAction
1029 """
1030 if act is None or not bool(res):
1031 return
1032
1033 url = QUrl(res["action"])
1034 method = res["method"]
1035
1036 if not url.isEmpty() and method in ["get", "post"]:
1037 act.setVisible(True)
1038 act.setText(self.tr("Add to web search toolbar"))
1039 act.triggered.connect(self.__addSearchEngine)
1040
1041 def __isUrlValid(self, url):
1042 """
1043 Private method to check a URL for validity.
1044
1045 @param url URL to be checked (QUrl)
1046 @return flag indicating a valid URL (boolean)
1047 """
1048 return url.isValid() and \
1049 bool(url.host()) and \
1050 bool(url.scheme()) and \
1051 "." in url.host()
1052
1053 def __replaceMisspelledWord(self, act):
1054 """
1055 Private slot to replace a misspelled word under the context menu.
1056
1057 @param act reference to the action that triggered
1058 @type QAction
1059 """
1060 suggestion = act.text()
1061 self.page().replaceMisspelledWord(suggestion)
1062
1063 def __openLinkInNewTab(self, act):
1064 """
1065 Private method called by the context menu to open a link in a new
1066 tab.
1067
1068 @param act reference to the action that triggered
1069 @type QAction
1070 """
1071 url = act.data()
1072 if url.isEmpty():
1073 return
1074
1075 self.setSource(url, newTab=True)
1076
1077 def __openLinkInNewWindow(self, act):
1078 """
1079 Private slot called by the context menu to open a link in a new
1080 window.
1081
1082 @param act reference to the action that triggered
1083 @type QAction
1084 """
1085 url = act.data()
1086 if url.isEmpty():
1087 return
1088
1089 self.__mw.newWindow(url)
1090
1091 def __openLinkInNewPrivateWindow(self, act):
1092 """
1093 Private slot called by the context menu to open a link in a new
1094 private window.
1095
1096 @param act reference to the action that triggered
1097 @type QAction
1098 """
1099 url = act.data()
1100 if url.isEmpty():
1101 return
1102
1103 self.__mw.newPrivateWindow(url)
1104
1105 def __bookmarkLink(self, act):
1106 """
1107 Private slot to bookmark a link via the context menu.
1108
1109 @param act reference to the action that triggered
1110 @type QAction
1111 """
1112 url = act.data()
1113 if url.isEmpty():
1114 return
1115
1116 from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
1117 dlg = AddBookmarkDialog()
1118 dlg.setUrl(bytes(url.toEncoded()).decode())
1119 dlg.exec_()
1120
1121 def __sendLink(self, act):
1122 """
1123 Private slot to send a link via email.
1124
1125 @param act reference to the action that triggered
1126 @type QAction
1127 """
1128 data = act.data()
1129 if isinstance(data, QUrl) and data.isEmpty():
1130 return
1131
1132 if isinstance(data, QUrl):
1133 data = data.toString()
1134 QDesktopServices.openUrl(QUrl("mailto:?body=" + data))
1135
1136 def __copyLink(self, act):
1137 """
1138 Private slot to copy a link to the clipboard.
1139
1140 @param act reference to the action that triggered
1141 @type QAction
1142 """
1143 data = act.data()
1144 if isinstance(data, QUrl) and data.isEmpty():
1145 return
1146
1147 if isinstance(data, QUrl):
1148 data = data.toString()
1149 QApplication.clipboard().setText(data)
1150
1151 def __downloadLink(self):
1152 """
1153 Private slot to download a link and save it to disk.
1154 """
1155 self.triggerPageAction(QWebEnginePage.DownloadLinkToDisk)
1156
1157 def __downloadImage(self):
1158 """
1159 Private slot to download an image and save it to disk.
1160 """
1161 self.triggerPageAction(QWebEnginePage.DownloadImageToDisk)
1162
1163 def __copyImage(self):
1164 """
1165 Private slot to copy an image to the clipboard.
1166 """
1167 self.triggerPageAction(QWebEnginePage.CopyImageToClipboard)
1168
1169 def __blockImage(self, act):
1170 """
1171 Private slot to add a block rule for an image URL.
1172
1173 @param act reference to the action that triggered
1174 @type QAction
1175 """
1176 url = act.data()
1177 dlg = WebBrowserWindow.adBlockManager().showDialog()
1178 dlg.addCustomRule(url)
1179
1180 def __searchImage(self, act):
1181 """
1182 Private slot to search for an image URL.
1183
1184 @param act reference to the action that triggered
1185 @type QAction
1186 """
1187 url = act.data()
1188 self.setSource(url, newTab=True)
1189
1190 def __downloadMedia(self):
1191 """
1192 Private slot to download a media and save it to disk.
1193 """
1194 self.triggerPageAction(QWebEnginePage.DownloadMediaToDisk)
1195
1196 def __pauseMedia(self):
1197 """
1198 Private slot to pause or play the selected media.
1199 """
1200 self.triggerPageAction(QWebEnginePage.ToggleMediaPlayPause)
1201
1202 def __muteMedia(self):
1203 """
1204 Private slot to (un)mute the selected media.
1205 """
1206 self.triggerPageAction(QWebEnginePage.ToggleMediaMute)
1207
1208 def __virusTotal(self, act):
1209 """
1210 Private slot to scan the selected URL with VirusTotal.
1211
1212 @param act reference to the action that triggered
1213 @type QAction
1214 """
1215 url = act.data()
1216 self.__mw.requestVirusTotalScan(url)
1217
1218 def __searchDefaultRequested(self):
1219 """
1220 Private slot to search for some text with the current search engine.
1221 """
1222 searchText = self.selectedText()
1223
1224 if not searchText:
1225 return
1226
1227 engine = self.__mw.openSearchManager().currentEngine()
1228 if engine:
1229 self.search.emit(engine.searchUrl(searchText))
1230
1231 def __searchRequested(self, act):
1232 """
1233 Private slot to search for some text with a selected search engine.
1234
1235 @param act reference to the action that triggered this slot (QAction)
1236 """
1237 searchText = self.selectedText()
1238
1239 if not searchText:
1240 return
1241
1242 engineName = act.data()
1243 if engineName:
1244 engine = self.__mw.openSearchManager().engine(engineName)
1245 else:
1246 engine = self.__mw.openSearchManager().currentEngine()
1247 if engine:
1248 self.search.emit(engine.searchUrl(searchText))
1249
1250 def __addSearchEngine(self):
1251 """
1252 Private slot to add a new search engine.
1253 """
1254 from .Tools import Scripts
1255 script = Scripts.getFormData(self.__clickedPos)
1256 self.page().runJavaScript(
1257 script,
1258 WebBrowserPage.SafeJsWorld,
1259 lambda res: self.__mw.openSearchManager().addEngineFromForm(
1260 res, self))
1261
1262 def __webInspector(self):
1263 """
1264 Private slot to show the web inspector window.
1265 """
1266 from .WebInspector import WebInspector
1267 if WebInspector.isEnabled():
1268 if self.__inspector is None:
1269 self.__inspector = WebInspector()
1270 self.__inspector.setView(self, True)
1271 self.__inspector.inspectorClosed.connect(
1272 self.closeWebInspector)
1273 self.__inspector.show()
1274 else:
1275 self.closeWebInspector()
1276
1277 def closeWebInspector(self):
1278 """
1279 Public slot to close the web inspector.
1280 """
1281 if self.__inspector is not None:
1282 if self.__inspector.isVisible():
1283 self.__inspector.hide()
1284 WebInspector.unregisterView(self.__inspector)
1285 self.__inspector.deleteLater()
1286 self.__inspector = None
1287
1288 def addBookmark(self):
1289 """
1290 Public slot to bookmark the current page.
1291 """
1292 from .Tools import Scripts
1293 script = Scripts.getAllMetaAttributes()
1294 self.page().runJavaScript(
1295 script, WebBrowserPage.SafeJsWorld, self.__addBookmarkCallback)
1296
1297 def __addBookmarkCallback(self, res):
1298 """
1299 Private callback method of __addBookmark().
1300
1301 @param res reference to the result list containing all
1302 meta attributes
1303 @type list
1304 """
1305 description = ""
1306 for meta in res:
1307 if meta["name"] == "description":
1308 description = meta["content"]
1309
1310 from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
1311 dlg = AddBookmarkDialog()
1312 dlg.setUrl(bytes(self.url().toEncoded()).decode())
1313 dlg.setTitle(self.title())
1314 dlg.setDescription(description)
1315 dlg.exec_()
1316
1317 def dragEnterEvent(self, evt):
1318 """
1319 Protected method called by a drag enter event.
1320
1321 @param evt reference to the drag enter event (QDragEnterEvent)
1322 """
1323 evt.acceptProposedAction()
1324
1325 def dragMoveEvent(self, evt):
1326 """
1327 Protected method called by a drag move event.
1328
1329 @param evt reference to the drag move event (QDragMoveEvent)
1330 """
1331 evt.ignore()
1332 if evt.source() != self:
1333 if len(evt.mimeData().urls()) > 0:
1334 evt.acceptProposedAction()
1335 else:
1336 url = QUrl(evt.mimeData().text())
1337 if url.isValid():
1338 evt.acceptProposedAction()
1339
1340 if not evt.isAccepted():
1341 super(WebBrowserView, self).dragMoveEvent(evt)
1342
1343 def dropEvent(self, evt):
1344 """
1345 Protected method called by a drop event.
1346
1347 @param evt reference to the drop event (QDropEvent)
1348 """
1349 super(WebBrowserView, self).dropEvent(evt)
1350 if not evt.isAccepted() and \
1351 evt.source() != self and \
1352 evt.possibleActions() & Qt.CopyAction:
1353 url = QUrl()
1354 if len(evt.mimeData().urls()) > 0:
1355 url = evt.mimeData().urls()[0]
1356 if not url.isValid():
1357 url = QUrl(evt.mimeData().text())
1358 if url.isValid():
1359 self.setSource(url)
1360 evt.acceptProposedAction()
1361
1362 def _mousePressEvent(self, evt):
1363 """
1364 Protected method called by a mouse press event.
1365
1366 @param evt reference to the mouse event (QMouseEvent)
1367 """
1368 if WebBrowserWindow.autoScroller().mousePress(self, evt):
1369 evt.accept()
1370 return
1371
1372 self.__mw.setEventMouseButtons(evt.buttons())
1373 self.__mw.setEventKeyboardModifiers(evt.modifiers())
1374
1375 if evt.button() == Qt.XButton1:
1376 self.pageAction(QWebEnginePage.Back).trigger()
1377 evt.accept()
1378 elif evt.button() == Qt.XButton2:
1379 self.pageAction(QWebEnginePage.Forward).trigger()
1380 evt.accept()
1381
1382 def _mouseReleaseEvent(self, evt):
1383 """
1384 Protected method called by a mouse release event.
1385
1386 @param evt reference to the mouse event (QMouseEvent)
1387 """
1388 if WebBrowserWindow.autoScroller().mouseRelease(evt):
1389 evt.accept()
1390 return
1391
1392 accepted = evt.isAccepted()
1393 self.__page.event(evt)
1394 if not evt.isAccepted() and \
1395 self.__mw.eventMouseButtons() & Qt.MidButton:
1396 url = QUrl(QApplication.clipboard().text(QClipboard.Selection))
1397 if not url.isEmpty() and \
1398 url.isValid() and \
1399 url.scheme() != "":
1400 self.__mw.setEventMouseButtons(Qt.NoButton)
1401 self.__mw.setEventKeyboardModifiers(Qt.NoModifier)
1402 self.setSource(url)
1403 evt.setAccepted(accepted)
1404
1405 def _mouseMoveEvent(self, evt):
1406 """
1407 Protected method to handle mouse move events.
1408
1409 @param evt reference to the mouse event (QMouseEvent)
1410 """
1411 if self.__mw and self.__mw.isFullScreen():
1412 if self.__mw.isFullScreenNavigationVisible():
1413 self.__mw.hideFullScreenNavigation()
1414 elif evt.y() < 10:
1415 # mouse is within 10px to the top
1416 self.__mw.showFullScreenNavigation()
1417
1418 if WebBrowserWindow.autoScroller().mouseMove(evt):
1419 evt.accept()
1420
1421 def _wheelEvent(self, evt):
1422 """
1423 Protected method to handle wheel events.
1424
1425 @param evt reference to the wheel event (QWheelEvent)
1426 """
1427 if WebBrowserWindow.autoScroller().wheel():
1428 evt.accept()
1429 return
1430
1431 delta = evt.angleDelta().y()
1432 if evt.modifiers() & Qt.ControlModifier:
1433 if delta < 0:
1434 self.zoomOut()
1435 elif delta > 0:
1436 self.zoomIn()
1437 evt.accept()
1438
1439 elif evt.modifiers() & Qt.ShiftModifier:
1440 if delta < 0:
1441 self.backward()
1442 elif delta > 0:
1443 self.forward()
1444 evt.accept()
1445
1446 def _keyPressEvent(self, evt):
1447 """
1448 Protected method called by a key press.
1449
1450 @param evt reference to the key event (QKeyEvent)
1451 """
1452 if self.__mw.personalInformationManager().viewKeyPressEvent(self, evt):
1453 evt.accept()
1454 return
1455
1456 if evt.key() == Qt.Key_ZoomIn:
1457 self.zoomIn()
1458 evt.accept()
1459 elif evt.key() == Qt.Key_ZoomOut:
1460 self.zoomOut()
1461 evt.accept()
1462 elif evt.key() == Qt.Key_Plus:
1463 if evt.modifiers() & Qt.ControlModifier:
1464 self.zoomIn()
1465 evt.accept()
1466 elif evt.key() == Qt.Key_Minus:
1467 if evt.modifiers() & Qt.ControlModifier:
1468 self.zoomOut()
1469 evt.accept()
1470 elif evt.key() == Qt.Key_0:
1471 if evt.modifiers() & Qt.ControlModifier:
1472 self.zoomReset()
1473 evt.accept()
1474 elif evt.key() == Qt.Key_M:
1475 if evt.modifiers() & Qt.ControlModifier:
1476 self.__muteMedia()
1477 evt.accept()
1478 elif evt.key() == Qt.Key_Backspace:
1479 pos = QCursor.pos()
1480 pos = self.mapFromGlobal(pos)
1481 hitTest = self.page().hitTestContent(pos)
1482 if not hitTest.isContentEditable():
1483 self.pageAction(QWebEnginePage.Back).trigger()
1484 evt.accept()
1485
1486 def _keyReleaseEvent(self, evt):
1487 """
1488 Protected method called by a key release.
1489
1490 @param evt reference to the key event (QKeyEvent)
1491 """
1492 if evt.key() == Qt.Key_Escape:
1493 if self.isFullScreen():
1494 self.triggerPageAction(QWebEnginePage.ExitFullScreen)
1495 evt.accept()
1496 self.requestFullScreen(False)
1497
1498 def _gestureEvent(self, evt):
1499 """
1500 Protected method handling gesture events.
1501
1502 @param evt reference to the gesture event (QGestureEvent
1503 """
1504 pinch = evt.gesture(Qt.PinchGesture)
1505 if pinch:
1506 if pinch.state() == Qt.GestureStarted:
1507 pinch.setTotalScaleFactor(self.__currentZoom / 100.0)
1508 elif pinch.state() == Qt.GestureUpdated:
1509 scaleFactor = pinch.totalScaleFactor()
1510 self.setZoomValue(int(scaleFactor * 100))
1511 evt.accept()
1512
1513 def eventFilter(self, obj, evt):
1514 """
1515 Public method to process event for other objects.
1516
1517 @param obj reference to object to process events for
1518 @type QObject
1519 @param evt reference to event to be processed
1520 @type QEvent
1521 @return flag indicating that the event should be filtered out
1522 @rtype bool
1523 """
1524 if obj is self and evt.type() == QEvent.ParentChange and \
1525 self.parentWidget() is not None:
1526 self.parentWidget().installEventFilter(self)
1527
1528 # find the render widget receiving events for the web page
1529 if obj is self and evt.type() == QEvent.ChildAdded:
1530 if qVersionTuple() < (5, 8, 0):
1531 child = evt.child()
1532 if child and child.inherits(
1533 "QtWebEngineCore::"
1534 "RenderWidgetHostViewQtDelegateWidget"):
1535 self.__rwhvqt = child
1536 self.grabGesture(Qt.PinchGesture)
1537 self.__rwhvqt.grabGesture(Qt.PinchGesture)
1538 self.__rwhvqt.installEventFilter(self)
1539 elif qVersionTuple() >= (5, 11, 0):
1540 QTimer.singleShot(0, self.__setRwhvqt)
1541
1542 # forward events to WebBrowserView
1543 if obj is self.__rwhvqt and \
1544 evt.type() in [QEvent.KeyPress, QEvent.KeyRelease,
1545 QEvent.MouseButtonPress, QEvent.MouseButtonRelease,
1546 QEvent.MouseMove, QEvent.Wheel, QEvent.Gesture]:
1547 wasAccepted = evt.isAccepted()
1548 evt.setAccepted(False)
1549 if evt.type() == QEvent.KeyPress:
1550 self._keyPressEvent(evt)
1551 elif evt.type() == QEvent.KeyRelease:
1552 self._keyReleaseEvent(evt)
1553 elif evt.type() == QEvent.MouseButtonPress:
1554 self._mousePressEvent(evt)
1555 elif evt.type() == QEvent.MouseButtonRelease:
1556 self._mouseReleaseEvent(evt)
1557 elif evt.type() == QEvent.MouseMove:
1558 self._mouseMoveEvent(evt)
1559 elif evt.type() == QEvent.Wheel:
1560 self._wheelEvent(evt)
1561 elif evt.type() == QEvent.Gesture:
1562 self._gestureEvent(evt)
1563 ret = evt.isAccepted()
1564 evt.setAccepted(wasAccepted)
1565 return ret
1566
1567 if obj is self.parentWidget() and \
1568 evt.type() in [QEvent.KeyPress, QEvent.KeyRelease]:
1569 wasAccepted = evt.isAccepted()
1570 evt.setAccepted(False)
1571 if evt.type() == QEvent.KeyPress:
1572 self._keyPressEvent(evt)
1573 elif evt.type() == QEvent.KeyRelease:
1574 self._keyReleaseEvent(evt)
1575 ret = evt.isAccepted()
1576 evt.setAccepted(wasAccepted)
1577 return ret
1578
1579 # block already handled events
1580 if obj is self:
1581 if evt.type() in [QEvent.KeyPress, QEvent.KeyRelease,
1582 QEvent.MouseButtonPress,
1583 QEvent.MouseButtonRelease,
1584 QEvent.MouseMove, QEvent.Wheel, QEvent.Gesture]:
1585 return True
1586
1587 elif evt.type() == QEvent.Hide:
1588 if self.isFullScreen():
1589 self.triggerPageAction(QWebEnginePage.ExitFullScreen)
1590
1591 return super(WebBrowserView, self).eventFilter(obj, evt)
1592
1593 def event(self, evt):
1594 """
1595 Public method handling events.
1596
1597 @param evt reference to the event (QEvent)
1598 @return flag indicating, if the event was handled (boolean)
1599 """
1600 if evt.type() == QEvent.Gesture:
1601 self._gestureEvent(evt)
1602 return True
1603
1604 return super(WebBrowserView, self).event(evt)
1605
1606 def inputWidget(self):
1607 """
1608 Public method to get a reference to the render widget.
1609
1610 @return reference to the render widget
1611 @rtype QWidget
1612 """
1613 return self.__rwhvqt
1614
1615 def clearHistory(self):
1616 """
1617 Public slot to clear the history.
1618 """
1619 self.history().clear()
1620 self.__urlChanged(self.history().currentItem().url())
1621
1622 ###########################################################################
1623 ## Signal converters below
1624 ###########################################################################
1625
1626 def __urlChanged(self, url):
1627 """
1628 Private slot to handle the urlChanged signal.
1629
1630 @param url the new url (QUrl)
1631 """
1632 self.sourceChanged.emit(url)
1633
1634 self.forwardAvailable.emit(self.isForwardAvailable())
1635 self.backwardAvailable.emit(self.isBackwardAvailable())
1636
1637 def __iconUrlChanged(self, url):
1638 """
1639 Private slot to handle the iconUrlChanged signal.
1640
1641 @param url URL to get web site icon from
1642 @type QUrl
1643 """
1644 self.__siteIcon = QIcon()
1645 if self.__siteIconLoader is not None:
1646 self.__siteIconLoader.deleteLater()
1647 self.__siteIconLoader = WebIconLoader(url, self)
1648 self.__siteIconLoader.iconLoaded.connect(self.__iconLoaded)
1649
1650 def __iconLoaded(self, icon):
1651 """
1652 Private slot handling the loaded web site icon.
1653
1654 @param icon web site icon
1655 @type QIcon
1656 """
1657 self.__siteIcon = icon
1658
1659 from .Tools import WebIconProvider
1660 WebIconProvider.instance().saveIcon(self)
1661
1662 self.faviconChanged.emit()
1663
1664 def icon(self):
1665 """
1666 Public method to get the web site icon.
1667
1668 @return web site icon
1669 @rtype QIcon
1670 """
1671 if not self.__siteIcon.isNull():
1672 return QIcon(self.__siteIcon)
1673
1674 from .Tools import WebIconProvider
1675 return WebIconProvider.instance().iconForUrl(self.url())
1676
1677 def title(self):
1678 """
1679 Public method to get the view title.
1680
1681 @return view title
1682 @rtype str
1683 """
1684 titleStr = super(WebBrowserView, self).title()
1685 if not titleStr:
1686 if self.url().isEmpty():
1687 url = self.__page.requestedUrl()
1688 else:
1689 url = self.url()
1690
1691 titleStr = url.host()
1692 if not titleStr:
1693 titleStr = url.toString(QUrl.RemoveFragment)
1694
1695 if not titleStr or titleStr == "about:blank":
1696 titleStr = self.tr("Empty Page")
1697
1698 return titleStr
1699
1700 def __linkHovered(self, link):
1701 """
1702 Private slot to handle the linkHovered signal.
1703
1704 @param link the URL of the link (string)
1705 """
1706 self.highlighted.emit(link)
1707
1708 ###########################################################################
1709 ## Signal handlers below
1710 ###########################################################################
1711
1712 def __renderProcessTerminated(self, status, exitCode):
1713 """
1714 Private slot handling a crash of the web page render process.
1715
1716 @param status termination status
1717 @type QWebEnginePage.RenderProcessTerminationStatus
1718 @param exitCode exit code of the process
1719 @type int
1720 """
1721 if status == QWebEnginePage.NormalTerminationStatus:
1722 return
1723
1724 QTimer.singleShot(0, lambda: self.__showTabCrashPage(status))
1725
1726 def __showTabCrashPage(self, status):
1727 """
1728 Private slot to show the tab crash page.
1729
1730 @param status termination status
1731 @type QWebEnginePage.RenderProcessTerminationStatus
1732 """
1733 self.page().deleteLater()
1734 self.__createNewPage()
1735
1736 html = readAllFileContents(":/html/tabCrashPage.html")
1737 html = html.replace("@IMAGE@", pixmapToDataUrl(
1738 qApp.style().standardIcon(QStyle.SP_MessageBoxWarning).pixmap(
1739 48, 48)).toString())
1740 html = html.replace("@FAVICON@", pixmapToDataUrl(
1741 qApp.style() .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(
1742 16, 16)).toString())
1743 html = html.replace(
1744 "@TITLE@", self.tr("Render Process terminated abnormally"))
1745 html = html.replace(
1746 "@H1@", self.tr("Render Process terminated abnormally"))
1747 if status == QWebEnginePage.CrashedTerminationStatus:
1748 msg = self.tr("The render process crashed while"
1749 " loading this page.")
1750 elif status == QWebEnginePage.KilledTerminationStatus:
1751 msg = self.tr("The render process was killed.")
1752 else:
1753 msg = self.tr("The render process terminated while"
1754 " loading this page.")
1755 html = html.replace("@LI-1@", msg)
1756 html = html.replace(
1757 "@LI-2@",
1758 self.tr(
1759 "Try reloading the page or closing some tabs to make more"
1760 " memory available."))
1761 self.page().setHtml(html, self.url())
1762
1763 def __loadStarted(self):
1764 """
1765 Private method to handle the loadStarted signal.
1766 """
1767 # reset search
1768 self.findText("")
1769 self.__isLoading = True
1770 self.__progress = 0
1771
1772 def __loadProgress(self, progress):
1773 """
1774 Private method to handle the loadProgress signal.
1775
1776 @param progress progress value (integer)
1777 """
1778 self.__progress = progress
1779
1780 def __loadFinished(self, ok):
1781 """
1782 Private method to handle the loadFinished signal.
1783
1784 @param ok flag indicating the result (boolean)
1785 """
1786 self.__isLoading = False
1787 self.__progress = 0
1788
1789 QApplication.processEvents()
1790 QTimer.singleShot(200, self.__renderPreview)
1791
1792 from .ZoomManager import ZoomManager
1793 zoomValue = ZoomManager.instance().zoomValue(self.url())
1794 self.setZoomValue(zoomValue)
1795
1796 if ok:
1797 self.__mw.historyManager().addHistoryEntry(self)
1798 self.__mw.adBlockManager().page().hideBlockedPageEntries(
1799 self.page())
1800 self.__mw.passwordManager().completePage(self.page())
1801
1802 self.page().runJavaScript(
1803 "document.lastModified", WebBrowserPage.SafeJsWorld,
1804 lambda res: self.__adjustBookmark(res))
1805
1806 def __adjustBookmark(self, lastModified):
1807 """
1808 Private slot to adjust the 'lastModified' value of bookmarks.
1809
1810 @param lastModified last modified value
1811 @type str
1812 """
1813 modified = QDateTime.fromString(lastModified, "MM/dd/yyyy hh:mm:ss")
1814 if modified.isValid():
1815 from WebBrowser.WebBrowserWindow import WebBrowserWindow
1816 from .Bookmarks.BookmarkNode import BookmarkNode
1817 manager = WebBrowserWindow.bookmarksManager()
1818 for bookmark in manager.bookmarksForUrl(self.url()):
1819 manager.setTimestamp(bookmark, BookmarkNode.TsModified,
1820 modified)
1821
1822 def isLoading(self):
1823 """
1824 Public method to get the loading state.
1825
1826 @return flag indicating the loading state (boolean)
1827 """
1828 return self.__isLoading
1829
1830 def progress(self):
1831 """
1832 Public method to get the load progress.
1833
1834 @return load progress (integer)
1835 """
1836 return self.__progress
1837
1838 def __renderPreview(self):
1839 """
1840 Private slot to render a preview pixmap after the page was loaded.
1841 """
1842 from .WebBrowserSnap import renderTabPreview
1843 w = 600 # some default width, the preview gets scaled when shown
1844 h = int(w * self.height() / self.width())
1845 self.__preview = renderTabPreview(self, w, h)
1846
1847 def getPreview(self):
1848 """
1849 Public method to get the preview pixmap.
1850
1851 @return preview pixmap
1852 @rtype QPixmap
1853 """
1854 return self.__preview
1855
1856 def saveAs(self):
1857 """
1858 Public method to save the current page to a file.
1859 """
1860 url = self.url()
1861 if url.isEmpty():
1862 return
1863
1864 if qVersionTuple() >= (5, 8, 0) and PYQT_VERSION >= 0x50800:
1865 # since Qt 5.8.0
1866 fileName, savePageFormat = self.__getSavePageFileNameAndFormat()
1867 if fileName:
1868 self.page().save(fileName, savePageFormat)
1869 else:
1870 self.triggerPageAction(QWebEnginePage.SavePage)
1871
1872 def __getSavePageFileNameAndFormat(self):
1873 """
1874 Private method to get the file name to save the page to.
1875
1876 @return tuple containing the file name to save to and the
1877 save page format
1878 @rtype tuple of (str, QWebEngineDownloadItem.SavePageFormat)
1879 """
1880 documentLocation = QStandardPaths.writableLocation(
1881 QStandardPaths.DocumentsLocation)
1882 filterList = [
1883 self.tr("Web Archive (*.mhtml *.mht)"),
1884 self.tr("HTML File (*.html *.htm)"),
1885 self.tr("HTML File with all resources (*.html *.htm)"),
1886 ]
1887 extensionsList = [
1888 # tuple of extensions for *nix and Windows
1889 # keep in sync with filters list
1890 (".mhtml", ".mht"),
1891 (".html", ".htm"),
1892 (".html", ".htm"),
1893 ]
1894 if self.url().fileName():
1895 defaultFileName = os.path.join(documentLocation,
1896 self.url().fileName())
1897 else:
1898 defaultFileName = os.path.join(documentLocation,
1899 self.page().title())
1900 if Utilities.isWindowsPlatform():
1901 defaultFileName += ".mht"
1902 else:
1903 defaultFileName += ".mhtml"
1904
1905 fileName = ""
1906 saveFormat = QWebEngineDownloadItem.MimeHtmlSaveFormat
1907
1908 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
1909 None,
1910 self.tr("Save Web Page"),
1911 defaultFileName,
1912 ";;".join(filterList),
1913 None)
1914 if fileName:
1915 index = filterList.index(selectedFilter)
1916 if index == 0:
1917 saveFormat = QWebEngineDownloadItem.MimeHtmlSaveFormat
1918 elif index == 1:
1919 saveFormat = QWebEngineDownloadItem.SingleHtmlSaveFormat
1920 else:
1921 saveFormat = QWebEngineDownloadItem.CompleteHtmlSaveFormat
1922
1923 extension = os.path.splitext(fileName)[1]
1924 if not extension:
1925 # add the platform specific default extension
1926 if Utilities.isWindowsPlatform():
1927 extensionsIndex = 1
1928 else:
1929 extensionsIndex = 0
1930 extensions = extensionsList[index]
1931 fileName += extensions[extensionsIndex]
1932
1933 return fileName, saveFormat
1934
1935 ###########################################################################
1936 ## Miscellaneous methods below
1937 ###########################################################################
1938
1939 def createWindow(self, windowType):
1940 """
1941 Public method called, when a new window should be created.
1942
1943 @param windowType type of the requested window
1944 (QWebEnginePage.WebWindowType)
1945 @return reference to the created browser window (WebBrowserView)
1946 """
1947 if windowType in [QWebEnginePage.WebBrowserTab,
1948 QWebEnginePage.WebDialog]:
1949 return self.__mw.newTab(addNextTo=self)
1950 elif windowType == QWebEnginePage.WebBrowserWindow:
1951 return self.__mw.newWindow().currentBrowser()
1952 else:
1953 return self.__mw.newTab(addNextTo=self, background=True)
1954
1955 def preferencesChanged(self):
1956 """
1957 Public method to indicate a change of the settings.
1958 """
1959 self.reload()
1960
1961 ###########################################################################
1962 ## RSS related methods below
1963 ###########################################################################
1964
1965 def checkRSS(self):
1966 """
1967 Public method to check, if the loaded page contains feed links.
1968
1969 @return flag indicating the existence of feed links (boolean)
1970 """
1971 self.__rss = []
1972
1973 script = Scripts.getFeedLinks()
1974 feeds = self.page().execJavaScript(script)
1975
1976 if feeds is not None:
1977 for feed in feeds:
1978 if feed["url"] and feed["title"]:
1979 self.__rss.append((feed["title"], feed["url"]))
1980
1981 return len(self.__rss) > 0
1982
1983 def getRSS(self):
1984 """
1985 Public method to get the extracted RSS feeds.
1986
1987 @return list of RSS feeds (list of tuples of two strings)
1988 """
1989 return self.__rss
1990
1991 def hasRSS(self):
1992 """
1993 Public method to check, if the loaded page has RSS links.
1994
1995 @return flag indicating the presence of RSS links (boolean)
1996 """
1997 return len(self.__rss) > 0
1998
1999 ###########################################################################
2000 ## Full Screen handling below
2001 ###########################################################################
2002
2003 def isFullScreen(self):
2004 """
2005 Public method to check, if full screen mode is active.
2006
2007 @return flag indicating full screen mode
2008 @rtype bool
2009 """
2010 return self.__mw.isFullScreen()
2011
2012 def requestFullScreen(self, enable):
2013 """
2014 Public method to request full screen mode.
2015
2016 @param enable flag indicating full screen mode on or off
2017 @type bool
2018 """
2019 if enable:
2020 self.__mw.enterHtmlFullScreen()
2021 else:
2022 self.__mw.showNormal()
2023
2024 ###########################################################################
2025 ## Speed Dial slots below
2026 ###########################################################################
2027
2028 def __addSpeedDial(self):
2029 """
2030 Private slot to add a new speed dial.
2031 """
2032 self.__page.runJavaScript("addSpeedDial();",
2033 WebBrowserPage.SafeJsWorld)
2034
2035 def __configureSpeedDial(self):
2036 """
2037 Private slot to configure the speed dial.
2038 """
2039 self.page().runJavaScript("configureSpeedDial();",
2040 WebBrowserPage.SafeJsWorld)
2041
2042 def __reloadAllSpeedDials(self):
2043 """
2044 Private slot to reload all speed dials.
2045 """
2046 self.page().runJavaScript("reloadAll();", WebBrowserPage.SafeJsWorld)
2047
2048 def __resetSpeedDials(self):
2049 """
2050 Private slot to reset all speed dials to the default pages.
2051 """
2052 self.__speedDial.resetDials()
2053
2054 ###########################################################################
2055 ## Methods below implement session related functions
2056 ###########################################################################
2057
2058 def storeSessionData(self, data):
2059 """
2060 Public method to store session data to be restored later on.
2061
2062 @param data dictionary with session data to be restored
2063 @type dict
2064 """
2065 self.__restoreData = data
2066
2067 def __showEventSlot(self):
2068 """
2069 Private slot to perform actions when the view is shown and the event
2070 loop is running.
2071 """
2072 if self.__restoreData:
2073 sessionData, self.__restoreData = self.__restoreData, None
2074 self.loadFromSessionData(sessionData)
2075
2076 def showEvent(self, evt):
2077 """
2078 Protected method to handle show events.
2079
2080 @param evt reference to the show event object
2081 @type QShowEvent
2082 """
2083 super(WebBrowserView, self).showEvent(evt)
2084 self.activateSession()
2085
2086 def activateSession(self):
2087 """
2088 Public slot to activate a restored session.
2089 """
2090 if self.__restoreData and not self.__mw.isClosing():
2091 QTimer.singleShot(0, self.__showEventSlot)
2092
2093 def getSessionData(self):
2094 """
2095 Public method to populate the session data.
2096
2097 @return dictionary containing the session data
2098 @rtype dict
2099 """
2100 if self.__restoreData:
2101 # page has not been shown yet
2102 return self.__restoreData
2103
2104 sessionData = {}
2105 page = self.page()
2106
2107 # 1. zoom factor
2108 sessionData["ZoomFactor"] = page.zoomFactor()
2109
2110 # 2. scroll position
2111 scrollPos = page.scrollPosition()
2112 sessionData["ScrollPosition"] = {
2113 "x": scrollPos.x(),
2114 "y": scrollPos.y(),
2115 }
2116
2117 # 3. page history
2118 historyArray = QByteArray()
2119 stream = QDataStream(historyArray, QIODevice.WriteOnly)
2120 stream << page.history()
2121 sessionData["History"] = str(
2122 historyArray.toBase64(QByteArray.Base64UrlEncoding),
2123 encoding="ascii")
2124 sessionData["HistoryIndex"] = page.history().currentItemIndex()
2125
2126 # 4. current URL and title
2127 sessionData["Url"] = self.url().toString()
2128 sessionData["Title"] = self.title()
2129
2130 # 5. web icon
2131 iconArray = QByteArray()
2132 stream = QDataStream(iconArray, QIODevice.WriteOnly)
2133 stream << page.icon()
2134 sessionData["Icon"] = str(iconArray.toBase64(), encoding="ascii")
2135
2136 return sessionData
2137
2138 def loadFromSessionData(self, sessionData):
2139 """
2140 Public method to load the session data.
2141
2142 @param sessionData dictionary containing the session data as
2143 generated by getSessionData()
2144 @type dict
2145 """
2146 page = self.page()
2147 # blank the page
2148 page.setUrl(QUrl("about:blank"))
2149
2150 # 1. page history
2151 if "History" in sessionData:
2152 historyArray = QByteArray.fromBase64(
2153 sessionData["History"].encode("ascii"),
2154 QByteArray.Base64UrlEncoding)
2155 stream = QDataStream(historyArray, QIODevice.ReadOnly)
2156 stream >> page.history()
2157
2158 if "HistoryIndex" in sessionData:
2159 item = page.history().itemAt(sessionData["HistoryIndex"])
2160 if item is not None:
2161 page.history().goToItem(item)
2162
2163 # 2. zoom factor
2164 if "ZoomFactor" in sessionData:
2165 page.setZoomFactor(sessionData["ZoomFactor"])
2166
2167 # 3. scroll position
2168 if "ScrollPosition" in sessionData:
2169 scrollPos = sessionData["ScrollPosition"]
2170 page.scrollTo(QPointF(scrollPos["x"], scrollPos["y"]))
2171
2172 def extractSessionMetaData(self, sessionData):
2173 """
2174 Public method to extract some session meta data elements needed by the
2175 tab widget in case of deferred loading.
2176
2177 @param sessionData dictionary containing the session data as
2178 generated by getSessionData()
2179 @type dict
2180 @return tuple containing the title, URL and web icon
2181 @rtype tuple of (str, str, QIcon)
2182 """
2183 if "Title" in sessionData:
2184 title = sessionData["Title"]
2185 else:
2186 title = ""
2187
2188 if "Url" in sessionData:
2189 urlStr = sessionData["Url"]
2190 else:
2191 urlStr = ""
2192
2193 if "Icon" in sessionData:
2194 iconArray = QByteArray.fromBase64(
2195 sessionData["Icon"].encode("ascii"))
2196 stream = QDataStream(iconArray, QIODevice.ReadOnly)
2197 icon = QIcon()
2198 stream >> icon
2199 else:
2200 from .Tools import WebIconProvider
2201 icon = WebIconProvider.instance().iconForUrl(
2202 QUrl.fromUserInput(urlStr))
2203
2204 return title, urlStr, icon
2205
2206 ###########################################################################
2207 ## Methods below implement safe browsing related functions
2208 ###########################################################################
2209
2210 def getSafeBrowsingStatus(self):
2211 """
2212 Public method to get the safe browsing status of the current page.
2213
2214 @return flag indicating a safe site
2215 @rtype bool
2216 """
2217 if self.__page:
2218 return self.__page.getSafeBrowsingStatus()
2219 else:
2220 return True
2221
2222 ###########################################################################
2223 ## Methods below implement print support from the page
2224 ###########################################################################
2225
2226 @pyqtSlot()
2227 def __printPage(self):
2228 """
2229 Private slot to support printing from the web page.
2230 """
2231 self.__mw.tabWidget.printBrowser(browser=self)
2232
2233 ###########################################################################
2234 ## Methods below implement slots for Qt 5.11+
2235 ###########################################################################
2236
2237 if qVersionTuple() >= (5, 11, 0):
2238 @pyqtSlot("QWebEngineQuotaRequest")
2239 def __quotaRequested(self, quotaRequest):
2240 """
2241 Private slot to handle quota requests of the web page.
2242
2243 @param quotaRequest reference to the quota request object
2244 @type QWebEngineQuotaRequest
2245 """
2246 acceptRequest = Preferences.getWebBrowser("AcceptQuotaRequest")
2247 # yes/no/ask (0, 1, 2)
2248 if acceptRequest == 0:
2249 # always yes
2250 ok = True
2251 elif acceptRequest == 1:
2252 # always no
2253 ok = False
2254 else:
2255 # ask user
2256 from .Download.DownloadUtilities import dataString
2257 sizeStr = dataString(quotaRequest.requestedSize())
2258
2259 ok = E5MessageBox.yesNo(
2260 self,
2261 self.tr("Quota Request"),
2262 self.tr("""<p> Allow the website at <b>{0}</b> to use"""
2263 """ <b>{1}</b> of persistent storage?</p>""")
2264 .format(quotaRequest.origin().host(), sizeStr)
2265 )
2266
2267 if ok:
2268 quotaRequest.accept()
2269 else:
2270 quotaRequest.reject()
2271
2272 ###########################################################################
2273 ## Methods below implement slots for Qt 5.12+
2274 ###########################################################################
2275
2276 if qVersionTuple() >= (5, 12, 0):
2277 @pyqtSlot("QWebEngineClientCertificateSelection")
2278 def __selectClientCertificate(self, clientCertificateSelection):
2279 """
2280 Private slot to handle the client certificate selection request.
2281
2282 @param clientCertificateSelection list of client SSL certificates
2283 found in system's client certificate store
2284 @type QWebEngineClientCertificateSelection
2285 """
2286 certificates = clientCertificateSelection.certificates()
2287 if len(certificates) == 0:
2288 clientCertificateSelection.selectNone()
2289 elif len(certificates) == 1:
2290 clientCertificateSelection.select(certificates[0])
2291 else:
2292 certificate = None
2293 from E5Network.E5SslCertificateSelectionDialog import \
2294 E5SslCertificateSelectionDialog
2295 dlg = E5SslCertificateSelectionDialog(certificates, self)
2296 if dlg.exec_() == QDialog.Accepted:
2297 certificate = dlg.getSelectedCertificate()
2298
2299 if certificate is None:
2300 clientCertificateSelection.selectNone()
2301 else:
2302 clientCertificateSelection.select(certificate)

eric ide

mercurial