eric7/WebBrowser/WebBrowserView.py

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

eric ide

mercurial