예제 #1
0
class Fetch():
    data = QtCore.pyqtSignal(dict)

    def __init__(self, parent):
        self.session = QNetworkAccessManager(parent)
        self.cookies = QNetworkCookieJar()
        self.parent = parent
        self.session.setCookieJar(self.cookies)

    def base_handler(self, reply: QNetworkReply):
        try:
            response = json.loads(str(reply.readAll(), encoding='utf-8'))
            status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
        except:
            self.parent.warn.add_warn("Http解析错误")
            return
        if reply.error() != QNetworkReply.NoError:
            self.handler_error(response, status_code)
        else:
            self.data.emit(response)

    def get(self, url, param=None):
        url = QtCore.QUrl(parse_url(url, param))
        request = QNetworkRequest(url)
        reply = self.session.get(request)
        return reply

    def post(self, url, param=None, data=None, json=True):
        if isinstance(data, dict):
            f = ''
            for i in data:
                f += '{}={}&'.format(i, data[i])
            data = f[:-1]
        byte_data = QtCore.QByteArray()
        byte_data.append(data)
        url = QtCore.QUrl(parse_url(url, param))
        request = QNetworkRequest(url)
        if json:
            request.setHeader(QNetworkRequest.ContentTypeHeader, 'application/json')
        reply = self.session.post(request, byte_data)
        return reply

    def handler_error(self, response, status_code):
        if isinstance(response, dict):
            message = response.get('error', 'unknown')
            self.parent.warn.add_warn('网络请求错误,错误码为{},原因为{}'.format(status_code, message))
예제 #2
0
파일: browser.py 프로젝트: blropb/qt-test
 def __init__(self, parent):
     super().__init__(parent)
     self.set_url('http://google.ru')
     conn = QNetworkAccessManager()
     self.conn = conn
     self.r = QNetworkRequest()
     self.r.attribute(QNetworkRequest.CookieSaveControlAttribute, QVariant(True))
     # self.r.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")
     # self.r.setRawHeader("Referer", "http://www.facebook.com/")
     # self.r.setRawHeader("Host", "www.facebook.com")
     self.cj = QNetworkCookieJar()
     conn.setCookieJar(self.cj)
     conn.createRequest = self._create_request
     self.wv = WebView()
     self.wv.show()
     self.wv.page().setNetworkAccessManager(conn)
     # self.wv.auth()
     self.loop = QEventLoop()
     pass
예제 #3
0
        """Needed for accessing protected methods"""
        pass

else:
    _isSecureSSL = True  # That is handled by your browser
    nam = QNetworkAccessManager()
    MyCookieJar = None


def getFileNameForUrl(dlKey: str) -> str:
    return os.path.join(conf.currentPortalConfigDirectory,
                        sha1(dlKey.encode("UTF-8")).hexdigest())


if not isPyodide:
    nam.setCookieJar(MyCookieJar())

mimetypes.init()
if os.path.exists("mime.types"):
    mimetypes.types_map.update(mimetypes.read_mime_types("mime.types"))

# Source: http://srinikom.github.io/pyside-docs/PySide/QtNetwork/QNetworkReply.html
NetworkErrorDescrs = {
    "ConnectionRefusedError":
    "The remote server refused the connection (the server is not accepting requests)",
    "RemoteHostClosedError":
    "The remote server closed the connection prematurely, before the entire reply was received "
    "and processed",
    "HostNotFoundError":
    "The remote host name was not found (invalid hostname)",
    "TimeoutError":
예제 #4
0
class ScudCloud(QtWidgets.QMainWindow):

    forceClose = False
    messages = 0
    speller = Speller()
    title = 'ScudCloud'

    def __init__(self,
                 debug=False,
                 minimized=None,
                 urgent_hint=None,
                 settings_path='',
                 cache_path=''):
        super(ScudCloud, self).__init__(None)
        self.debug = debug
        self.minimized = minimized
        self.urgent_hint = urgent_hint
        self.setWindowTitle(self.title)
        self.settings_path = settings_path
        self.cache_path = cache_path
        self.notifier = Notifier(Resources.APP_NAME,
                                 Resources.get_path('scudcloud.png'))
        self.settings = QSettings(self.settings_path + '/scudcloud_qt5.cfg',
                                  QSettings.IniFormat)
        self.notifier.enabled = self.settings.value('Notifications',
                                                    defaultValue=True,
                                                    type=bool)
        self.identifier = self.settings.value("Domain")
        if Unity is not None:
            self.launcher = Unity.LauncherEntry.get_for_desktop_id(
                "scudcloud.desktop")
        else:
            self.launcher = DummyLauncher(self)
        self.webSettings()
        self.snippetsSettings()
        self.leftPane = LeftPane(self)
        self.stackedWidget = QtWidgets.QStackedWidget()
        centralWidget = QtWidgets.QWidget(self)
        layout = QtWidgets.QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.leftPane)
        layout.addWidget(self.stackedWidget)
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)
        self.startURL = Resources.SIGNIN_URL
        if self.identifier is not None:
            if isinstance(self.identifier, str):
                self.domains = self.identifier.split(",")
            else:
                self.domains = self.identifier
            self.startURL = self.normalize(self.domains[0])
        else:
            self.domains = []
        self.addWrapper(self.startURL)
        self.addMenu()
        self.tray = Systray(self)
        self.systray(self.minimized)
        self.installEventFilter(self)
        self.statusBar().showMessage('Loading Slack...')
        self.tickler = QTimer(self)
        self.tickler.setInterval(1800000)
        # Watch for ScreenLock events
        if DBusQtMainLoop is not None:
            DBusQtMainLoop(set_as_default=True)
            sessionBus = dbus.SessionBus()
            # Ubuntu 12.04 and other distros
            sessionBus.add_match_string(
                "type='signal',interface='org.gnome.ScreenSaver'")
            # Ubuntu 14.04
            sessionBus.add_match_string(
                "type='signal',interface='com.ubuntu.Upstart0_6'")
            # Ubuntu 16.04 and KDE
            sessionBus.add_match_string(
                "type='signal',interface='org.freedesktop.ScreenSaver'")
            # Cinnamon
            sessionBus.add_match_string(
                "type='signal',interface='org.cinnamon.ScreenSaver'")
            sessionBus.add_message_filter(self.screenListener)
            self.tickler.timeout.connect(self.sendTickle)
        # If dbus is not present, tickler timer will act like a blocker to not send tickle too often
        else:
            self.tickler.setSingleShot(True)
        self.tickler.start()

    def screenListener(self, bus, message):
        event = message.get_member()
        # "ActiveChanged" for Ubuntu 12.04 and other distros. "EventEmitted" for Ubuntu 14.04 and above
        if event == "ActiveChanged" or event == "EventEmitted":
            arg = message.get_args_list()[0]
            # True for Ubuntu 12.04 and other distros. "desktop-lock" for Ubuntu 14.04 and above
            if (arg == True
                    or arg == "desktop-lock") and self.tickler.isActive():
                self.tickler.stop()
            elif (arg == False
                  or arg == "desktop-unlock") and not self.tickler.isActive():
                self.sendTickle()
                self.tickler.start()

    def sendTickle(self):
        for i in range(0, self.stackedWidget.count()):
            self.stackedWidget.widget(i).sendTickle()

    def addWrapper(self, url):
        webView = Wrapper(self)
        webView.load(QtCore.QUrl(url))
        webView.show()
        webView.setZoomFactor(self.zoom)
        self.stackedWidget.addWidget(webView)
        self.stackedWidget.setCurrentWidget(webView)
        self.clearMemory()

    def webSettings(self):
        self.cookiesjar = PersistentCookieJar(self)
        self.zoom = self.readZoom()
        # We don't want Flash (it causes a lot of trouble in some distros)
        QWebSettings.globalSettings().setAttribute(QWebSettings.PluginsEnabled,
                                                   False)
        # We don't need Java
        QWebSettings.globalSettings().setAttribute(QWebSettings.JavaEnabled,
                                                   False)
        # Enabling Local Storage (now required by Slack)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.LocalStorageEnabled, True)
        # We need browsing history (required to not limit LocalStorage)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.PrivateBrowsingEnabled, False)
        # Enabling Cache
        self.diskCache = QNetworkDiskCache(self)
        self.diskCache.setCacheDirectory(self.cache_path)
        # Required for copy and paste clipboard integration
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.JavascriptCanAccessClipboard, True)
        # Enabling Inspeclet only when --debug=True (requires more CPU usage)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.DeveloperExtrasEnabled, self.debug)
        # Sharing the same networkAccessManager
        self.networkAccessManager = QNetworkAccessManager(self)
        self.networkAccessManager.setCookieJar(self.cookiesjar)
        self.networkAccessManager.setCache(self.diskCache)

    def snippetsSettings(self):
        self.disable_snippets = self.settings.value("Snippets")
        if self.disable_snippets is not None:
            self.disable_snippets = self.disable_snippets == "False"
        else:
            self.disable_snippets = False
        if self.disable_snippets:
            disable_snippets_css = ''
            with open(Resources.get_path('disable_snippets.css'), 'r') as f:
                disable_snippets_css = f.read()
            with open(os.path.join(self.cache_path, 'resources.css'),
                      'a') as f:
                f.write(disable_snippets_css)

    def toggleFullScreen(self):
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def toggleMenuBar(self):
        menu = self.menuBar()
        state = menu.isHidden()
        menu.setVisible(state)
        if state:
            self.settings.setValue("Menu", "True")
        else:
            self.settings.setValue("Menu", "False")

    def restore(self):
        geometry = self.settings.value("geometry")
        if geometry is not None:
            self.restoreGeometry(geometry)
        windowState = self.settings.value("windowState")
        if windowState is not None:
            self.restoreState(windowState)
        else:
            self.setWindowState(QtCore.Qt.WindowMaximized)

    def systray(self, show=None):
        if show is None:
            show = self.settings.value("Systray") == "True"
        if show:
            self.tray.show()
            self.menus["file"]["close"].setEnabled(True)
            self.settings.setValue("Systray", "True")
        else:
            self.tray.setVisible(False)
            self.menus["file"]["close"].setEnabled(False)
            self.settings.setValue("Systray", "False")

    def readZoom(self):
        default = 1
        if self.settings.value("Zoom") is not None:
            default = float(self.settings.value("Zoom"))
        return default

    def setZoom(self, factor=1):
        if factor > 0:
            for i in range(0, self.stackedWidget.count()):
                widget = self.stackedWidget.widget(i)
                widget.setZoomFactor(factor)
            self.settings.setValue("Zoom", factor)

    def zoomIn(self):
        self.setZoom(self.current().zoomFactor() + 0.1)

    def zoomOut(self):
        self.setZoom(self.current().zoomFactor() - 0.1)

    def zoomReset(self):
        self.setZoom()

    def addTeam(self):
        self.switchTo(Resources.SIGNIN_URL)

    def addMenu(self):
        # We'll register the webpage shorcuts with the window too (Fixes #338)
        undo = self.current().pageAction(QWebPage.Undo)
        redo = self.current().pageAction(QWebPage.Redo)
        cut = self.current().pageAction(QWebPage.Cut)
        copy = self.current().pageAction(QWebPage.Copy)
        paste = self.current().pageAction(QWebPage.Paste)
        back = self.current().pageAction(QWebPage.Back)
        forward = self.current().pageAction(QWebPage.Forward)
        reload = self.current().pageAction(QWebPage.Reload)
        self.menus = {
            "file": {
                "preferences":
                self.createAction("Preferences",
                                  lambda: self.current().preferences()),
                "systray":
                self.createAction("Close to Tray", self.systray, None, True),
                "addTeam":
                self.createAction("Sign in to Another Team",
                                  lambda: self.addTeam()),
                "signout":
                self.createAction("Signout", lambda: self.current().logout()),
                "close":
                self.createAction("Close", self.close, QKeySequence.Close),
                "exit":
                self.createAction("Quit", self.exit, QKeySequence.Quit)
            },
            "edit": {
                "undo":
                self.createAction(
                    undo.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Undo),
                    undo.shortcut()),
                "redo":
                self.createAction(
                    redo.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Redo),
                    redo.shortcut()),
                "cut":
                self.createAction(
                    cut.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Cut),
                    cut.shortcut()),
                "copy":
                self.createAction(
                    copy.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Copy),
                    copy.shortcut()),
                "paste":
                self.createAction(
                    paste.text(), lambda: self.current().page().triggerAction(
                        QWebPage.Paste), paste.shortcut()),
                "back":
                self.createAction(
                    back.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Back),
                    back.shortcut()),
                "forward":
                self.createAction(
                    forward.text(), lambda: self.current().page().
                    triggerAction(QWebPage.Forward), forward.shortcut()),
                "reload":
                self.createAction(
                    reload.text(), lambda: self.current().page().triggerAction(
                        QWebPage.Reload), reload.shortcut()),
            },
            "view": {
                "zoomin":
                self.createAction("Zoom In", self.zoomIn, QKeySequence.ZoomIn),
                "zoomout":
                self.createAction("Zoom Out", self.zoomOut,
                                  QKeySequence.ZoomOut),
                "reset":
                self.createAction("Reset", self.zoomReset,
                                  QtCore.Qt.CTRL + QtCore.Qt.Key_0),
                "fullscreen":
                self.createAction("Toggle Full Screen", self.toggleFullScreen,
                                  QtCore.Qt.Key_F11),
                "hidemenu":
                self.createAction("Toggle Menubar", self.toggleMenuBar,
                                  QtCore.Qt.Key_F12)
            },
            "help": {
                "help":
                self.createAction("Help and Feedback",
                                  lambda: self.current().help(),
                                  QKeySequence.HelpContents),
                "center":
                self.createAction("Slack Help Center",
                                  lambda: self.current().helpCenter()),
                "about":
                self.createAction("About", lambda: self.current().about())
            }
        }
        menu = self.menuBar()
        fileMenu = menu.addMenu("&File")
        fileMenu.addAction(self.menus["file"]["preferences"])
        fileMenu.addAction(self.menus["file"]["systray"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["addTeam"])
        fileMenu.addAction(self.menus["file"]["signout"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["close"])
        fileMenu.addAction(self.menus["file"]["exit"])
        editMenu = menu.addMenu("&Edit")
        editMenu.addAction(self.menus["edit"]["undo"])
        editMenu.addAction(self.menus["edit"]["redo"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["cut"])
        editMenu.addAction(self.menus["edit"]["copy"])
        editMenu.addAction(self.menus["edit"]["paste"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["back"])
        editMenu.addAction(self.menus["edit"]["forward"])
        editMenu.addAction(self.menus["edit"]["reload"])
        viewMenu = menu.addMenu("&View")
        viewMenu.addAction(self.menus["view"]["zoomin"])
        viewMenu.addAction(self.menus["view"]["zoomout"])
        viewMenu.addAction(self.menus["view"]["reset"])
        viewMenu.addSeparator()
        viewMenu.addAction(self.menus["view"]["fullscreen"])
        viewMenu.addAction(self.menus["view"]["hidemenu"])
        helpMenu = menu.addMenu("&Help")
        helpMenu.addAction(self.menus["help"]["help"])
        helpMenu.addAction(self.menus["help"]["center"])
        helpMenu.addSeparator()
        helpMenu.addAction(self.menus["help"]["about"])
        self.enableMenus(False)
        showSystray = self.settings.value("Systray") == "True"
        self.menus["file"]["systray"].setChecked(showSystray)
        self.menus["file"]["close"].setEnabled(showSystray)
        # Restore menu visibility
        visible = self.settings.value("Menu")
        if visible is not None and visible == "False":
            menu.setVisible(False)

    def enableMenus(self, enabled):
        self.menus["file"]["preferences"].setEnabled(enabled == True)
        self.menus["file"]["addTeam"].setEnabled(enabled == True)
        self.menus["file"]["signout"].setEnabled(enabled == True)
        self.menus["help"]["help"].setEnabled(enabled == True)

    def createAction(self, text, slot, shortcut=None, checkable=False):
        action = QtWidgets.QAction(text, self)
        action.triggered.connect(slot)
        if shortcut is not None:
            action.setShortcut(shortcut)
            self.addAction(action)
        if checkable:
            action.setCheckable(True)
        return action

    def normalize(self, url):
        if url.endswith(".slack.com"):
            url += "/"
        elif not url.endswith(".slack.com/"):
            url = "https://" + url + ".slack.com/"
        return url

    def current(self):
        return self.stackedWidget.currentWidget()

    def teams(self, teams):
        if len(self.domains) == 0:
            self.domains.append(teams[0]['team_url'])
        team_list = [t['team_url'] for t in teams]
        for t in teams:
            for i in range(0, len(self.domains)):
                self.domains[i] = self.normalize(self.domains[i])
                # When team_icon is missing, the team already exists (Fixes #381, #391)
                if 'team_icon' in t:
                    if self.domains[i] in team_list:
                        add = next(item for item in teams
                                   if item['team_url'] == self.domains[i])
                        if 'team_icon' in add:
                            self.leftPane.addTeam(add['id'], add['team_name'],
                                                  add['team_url'],
                                                  add['team_icon']['image_44'],
                                                  add == teams[0])
                            # Adding new teams and saving loading positions
                            if t['team_url'] not in self.domains:
                                self.leftPane.addTeam(
                                    t['id'], t['team_name'], t['team_url'],
                                    t['team_icon']['image_44'], t == teams[0])
                                self.domains.append(t['team_url'])
                                self.settings.setValue("Domain", self.domains)
        if len(teams) > 1:
            self.leftPane.show()

    def switchTo(self, url):
        exists = False
        for i in range(0, self.stackedWidget.count()):
            if self.stackedWidget.widget(i).url().toString().startswith(url):
                self.stackedWidget.setCurrentIndex(i)
                self.quicklist(self.current().listChannels())
                self.current().setFocus()
                self.leftPane.click(i)
                self.clearMemory()
                exists = True
                break
        if not exists:
            self.addWrapper(url)

    def eventFilter(self, obj, event):
        if event.type(
        ) == QtCore.QEvent.ActivationChange and self.isActiveWindow():
            self.focusInEvent(event)
        if event.type() == QtCore.QEvent.KeyPress:
            # Ctrl + <n>
            modifiers = QtWidgets.QApplication.keyboardModifiers()
            if modifiers == QtCore.Qt.ControlModifier:
                if event.key() == QtCore.Qt.Key_1: self.leftPane.click(0)
                elif event.key() == QtCore.Qt.Key_2: self.leftPane.click(1)
                elif event.key() == QtCore.Qt.Key_3: self.leftPane.click(2)
                elif event.key() == QtCore.Qt.Key_4: self.leftPane.click(3)
                elif event.key() == QtCore.Qt.Key_5: self.leftPane.click(4)
                elif event.key() == QtCore.Qt.Key_6: self.leftPane.click(5)
                elif event.key() == QtCore.Qt.Key_7: self.leftPane.click(6)
                elif event.key() == QtCore.Qt.Key_8: self.leftPane.click(7)
                elif event.key() == QtCore.Qt.Key_9:
                    self.leftPane.click(8)
                    # Ctrl + Tab
                elif event.key() == QtCore.Qt.Key_Tab:
                    self.leftPane.clickNext(1)
            # Ctrl + BackTab
            if (modifiers & QtCore.Qt.ControlModifier) and (
                    modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_Backtab:
                    self.leftPane.clickNext(-1)
            # Ctrl + Shift + <key>
            if (modifiers & QtCore.Qt.ShiftModifier) and (
                    modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_V:
                    self.current().createSnippet()
        return QtWidgets.QMainWindow.eventFilter(self, obj, event)

    def focusInEvent(self, event):
        self.launcher.set_property("urgent", False)
        self.tray.stopAlert()
        # Let's tickle all teams on window focus, but only if tickle was not fired in last 30 minutes
        if DBusQtMainLoop is None and not self.tickler.isActive():
            self.sendTickle()
            self.tickler.start()

    def titleChanged(self):
        self.setWindowTitle(self.current().title())

    def setForceClose(self):
        self.forceClose = True

    def closeEvent(self, event):
        if not self.forceClose and self.settings.value("Systray") == "True":
            self.hide()
            event.ignore()
        else:
            self.cookiesjar.save()
            self.settings.setValue("Domain", self.domains)
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            self.settings.setValue("Domain", self.domains)
        self.forceClose = False

    def show(self):
        self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized
                            | QtCore.Qt.WindowActive)
        self.activateWindow()
        self.setVisible(True)

    def exit(self):
        # Make sure tray is not visible (Fixes #513)
        self.tray.setVisible(False)
        self.setForceClose()
        self.close()

    def quicklist(self, channels):
        if Dbusmenu is not None:
            if channels is not None:
                ql = Dbusmenu.Menuitem.new()
                self.launcher.set_property("quicklist", ql)
                for c in channels:
                    if type(c) is dict and hasattr(
                            c, '__getitem__') and c['is_member']:
                        item = Dbusmenu.Menuitem.new()
                        item.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
                                          "#" + c['name'])
                        item.property_set("id", c['name'])
                        item.property_set_bool(Dbusmenu.MENUITEM_PROP_VISIBLE,
                                               True)
                        item.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
                                     self.current().openChannel)
                        ql.child_append(item)
                self.launcher.set_property("quicklist", ql)

    def notify(self, title, message, icon):
        if self.debug:
            print("Notification: title [{}] message [{}] icon [{}]".format(
                title, message, icon))
        self.notifier.notify(title, message, icon)
        self.alert()

    def alert(self):
        if not self.isActiveWindow():
            self.launcher.set_property("urgent", True)
            self.tray.alert()
        if self.urgent_hint is True:
            QApplication.alert(self)

    def count(self):
        total = 0
        unreads = 0
        for i in range(0, self.stackedWidget.count()):
            widget = self.stackedWidget.widget(i)
            highlights = widget.highlights
            unreads += widget.unreads
            total += highlights
        if total > self.messages:
            self.alert()
        if 0 == total:
            self.launcher.set_property("count_visible", False)
            self.tray.setCounter(0)
            if unreads > 0:
                self.setWindowTitle("*{}".format(self.title))
            else:
                self.setWindowTitle(self.title)
        else:
            self.tray.setCounter(total)
            self.launcher.set_property("count", total)
            self.launcher.set_property("count_visible", True)
            self.setWindowTitle("[{}]{}".format(str(total), self.title))
        self.messages = total

    def clearMemory(self):
        QWebSettings.globalSettings().clearMemoryCaches()
        QWebSettings.globalSettings().clearIconDatabase()
예제 #5
0
class _WebEngineRendererHelper(QObject):
    """
    This helper class is doing the real work. It is required to
    allow WebEngineRenderer.render() to be called "asynchronously"
    (but always from Qt's GUI thread).
    """
    def __init__(self, parent):
        """
        Copies the properties from the parent (WebEngineRenderer) object,
        creates the required instances of QWebPage, QWebView and QMainWindow
        and registers some Slots.
        """
        QObject.__init__(self)

        # Copy properties from parent
        for key, value in parent.__dict__.items():
            setattr(self, key, value)

        # Determine Proxy settings
        proxy = QNetworkProxy(QNetworkProxy.NoProxy)
        if 'http_proxy' in os.environ:
            proxy_url = QUrl(os.environ['http_proxy'])
            if proxy_url.scheme().startswith('http'):
                protocol = QNetworkProxy.HttpProxy
            else:
                protocol = QNetworkProxy.Socks5Proxy

            proxy = QNetworkProxy(protocol, proxy_url.host(), proxy_url.port(),
                                  proxy_url.userName(), proxy_url.password())

        # Create and connect required PyQt5 objects
        self._page = CustomWebPage(logger=self.logger,
                                   ignore_alert=self.ignoreAlert,
                                   ignore_confirm=self.ignoreConfirm,
                                   ignore_prompt=self.ignorePrompt,
                                   interrupt_js=self.interruptJavaScript)
        self._qt_proxy = QNetworkProxy()
        self._qt_proxy.setApplicationProxy(proxy)
        self._view = QWebEngineView()
        self._view.setPage(self._page)
        _window_flags = Qt.WindowFlags()
        self._window = QMainWindow(flags=_window_flags)
        self._window.setCentralWidget(self._view)
        self._qt_network_access_manager = QNetworkAccessManager()

        # Import QWebSettings
        for key, value in self.qWebSettings.items():
            self._page.settings().setAttribute(key, value)

        # Connect required event listeners
        # assert False, "Not finish"
        self._page.loadFinished.connect(self._on_load_finished)
        self._page.loadStarted.connect(self._on_load_started)
        self._qt_network_access_manager.sslErrors.connect(self._on_ssl_errors)
        self._qt_network_access_manager.finished.connect(self._on_each_reply)

        # The way we will use this, it seems to be unnecessary to have Scrollbars enabled
        self._scroll_area = QAbstractScrollArea()
        self._scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Show this widget
        self._window.show()

    def __del__(self):
        """
        Clean up Qt5 objects.
        """
        self._window.close()
        del self._window
        del self._view
        del self._page

    def render(self, res):
        """
        The real worker. Loads the page (_load_page) and awaits
        the end of the given 'delay'. While it is waiting outstanding
        QApplication events are processed.
        After the given delay, the Window or Widget (depends
        on the value of 'grabWholeWindow' is drawn into a QScreen
        and post processed (_post_process_image).
        """
        self._load_page(res, self.width, self.height, self.timeout)
        # Wait for end of timer. In this time, process
        # other outstanding Qt events.
        if self.wait > 0:
            if self.logger:
                self.logger.debug("Waiting {} seconds ".format(self.wait))
            wait_to_time = time.time() + self.wait
            while time.time() < wait_to_time:
                if self.app.hasPendingEvents():
                    self.app.processEvents()

        if self.renderTransparentBackground:
            # Another possible drawing solution
            image = QImage(self._page.viewportSize(), QImage.Format_ARGB32)
            image.fill(QColor(255, 0, 0, 0).rgba())

            # http://ariya.blogspot.com/2009/04/transparent-qwebview-and-qwebpage.html
            palette = self._view.palette()
            palette.setBrush(QPalette.Base, Qt.transparent)
            self._page.setPalette(palette)
            self._view.setAttribute(Qt.WA_OpaquePaintEvent, False)

            painter = QPainter(image)
            painter.setBackgroundMode(Qt.TransparentMode)
            self._window.render(painter)
            painter.end()
        else:
            if self.grabWholeWindow:
                # Note that this does not fully ensure that the
                # window still has the focus when the screen is
                # grabbed. This might result in a race condition.
                self._view.activateWindow()
                qt_screen = self.app.primaryScreen()
                image = qt_screen.grabWindow(sip.voidptr(0))
            else:
                image = self._window.grab()

        return self._post_process_image(image)

    def _load_page(self, res, width, height, timeout):
        """
        This method implements the logic for retrieving and displaying
        the requested page.
        """

        # This is an event-based application. So we have to wait until
        # "loadFinished(bool)" raised.
        cancel_at = time.time() + timeout
        self.__loading = True
        self.__loadingResult = False  # Default

        # When "res" is of type tuple, it has two elements where the first
        # element is the HTML code to render and the second element is a string
        # setting the base URL for the interpreted HTML code.
        # When resource is of type str or unicode, it is handled as URL which
        # shall be loaded
        if type(res) == tuple:
            url = res[1]
        else:
            url = res

        if self.encodedUrl:
            qt_url = QUrl().fromEncoded(url)
        else:
            qt_url = QUrl(url)

        # Set the required cookies, if any
        self.cookieJar = CookieJar(self.cookies, qt_url)
        self._qt_network_access_manager.setCookieJar(self.cookieJar)

        # Load the page
        if type(res) == tuple:
            self._page.setHtml(res[0], qt_url)  # HTML, baseUrl
        else:
            self._page.load(qt_url)

        while self.__loading:
            if timeout > 0 and time.time() >= cancel_at:
                raise RuntimeError("Request timed out on {}".format(res))
            while self.app.hasPendingEvents() and self.__loading:
                self.app.processEvents()

        if self.logger:
            self.logger.debug("Processing result")

        if not self.__loading_result:
            if self.logger:
                self.logger.warning("Failed to load {}".format(res))

        # Set initial viewport (the size of the "window")
        size = self._page.contentsSize()
        if self.logger:
            self.logger.debug("contentsSize: %s", size)
        if width > 0:
            size.setWidth(width)
        if height > 0:
            size.setHeight(height)

        self._window.resize(size.toSize())

    def _post_process_image(self, q_image):
        """
        If 'scaleToWidth' or 'scaleToHeight' are set to a value
        greater than zero this method will scale the image
        using the method defined in 'scaleRatio'.
        """
        if self.scaleToWidth > 0 or self.scaleToHeight > 0:
            # Scale this image
            if self.scaleRatio == 'keep':
                ratio = Qt.KeepAspectRatio
            elif self.scaleRatio in ['expand', 'crop']:
                ratio = Qt.KeepAspectRatioByExpanding
            else:  # 'ignore'
                ratio = Qt.IgnoreAspectRatio
            q_image = q_image.scaled(self.scaleToWidth, self.scaleToHeight,
                                     ratio, Qt.SmoothTransformation)
            if self.scaleRatio == 'crop':
                q_image = q_image.copy(0, 0, self.scaleToWidth,
                                       self.scaleToHeight)
        return q_image

    @pyqtSlot(QNetworkReply, name='finished')
    def _on_each_reply(self, reply):
        """
        Logs each requested uri
        """
        self.logger.debug("Received {}".format(reply.url().toString()))

    # Event for "loadStarted()" signal
    @pyqtSlot(name='loadStarted')
    def _on_load_started(self):
        """
        Slot that sets the '__loading' property to true
        """
        if self.logger:
            self.logger.debug("loading started")
        self.__loading = True

    # Event for "loadFinished(bool)" signal
    @pyqtSlot(bool, name='loadFinished')
    def _on_load_finished(self, result):
        """
        Slot that sets the '__loading' property to false and stores
        the result code in '__loading_result'.
        """
        if self.logger:
            self.logger.debug("loading finished with result %s", result)
        self.__loading = False
        self.__loading_result = result

    # Event for "sslErrors(QNetworkReply *,const QList<QSslError>&)" signal
    @pyqtSlot('QNetworkReply*', 'QList<QSslError>', name='sslErrors')
    def _on_ssl_errors(self, reply, errors):
        """
        Slot that writes SSL warnings into the log but ignores them.
        """
        for e in errors:
            if self.logger:
                self.logger.warn("SSL: " + e.errorString())
        reply.ignoreSslErrors()

    @property
    def window(self):
        return self._window
예제 #6
0
class GreasemonkeyBridge(QObject):
    """Object to be exposed to greasemonkey clients in javascript."""

    requestFinished = pyqtSignal(QVariant)

    def __init__(self, wc, profile):
        super().__init__()
        self.wc = wc
        self.profile = profile
        # It would be nice to use the qnam from the current profile but
        # that ain't an option and it looks like webengine doesn't even
        # use it anymore. So we have to try copy various things ourself.
        self.nam = QNetworkAccessManager(self)
        self.cookiejar = CookieJarWrapper(self, self.profile.cookieStore())
        self.nam.setCookieJar(self.cookiejar)

    def handle_xhr_reply(self, reply, index):
        ret = {}
        ret['_qute_gm_request_index'] = index
        ret['status'] = reply.attribute(
            QNetworkRequest.HttpStatusCodeAttribute)
        ret['statusText'] = reply.attribute(
            QNetworkRequest.HttpReasonPhraseAttribute)
        ret['responseText'] = reply.readAll()
        # list of QByteArray tuples
        heads = reply.rawHeaderPairs()
        pyheads = [(str(h[0], encoding='ascii'), str(h[1], encoding='ascii'))
                   for h in heads]
        for k, v in pyheads:
            print("{}: {}".format(k, v))
        ret['responseHeaders'] = dict(pyheads)
        ret['finalUrl'] = reply.url()
        self.requestFinished.emit(ret)

    @pyqtSlot(QVariant, result=QVariant)
    def GM_xmlhttpRequest(self, details):
        # we could actually mock a XMLHttpRequest to support progress
        # signals but who really uses them?
        # https://wiki.greasespot.net/GM_xmlhttpRequest
        # qtwebchannel.js calls JSON.stringify in QWebChannel.send() so any
        # method attributes of arguments (eg {'onload':function(...){...;}) are
        # stripped.
        # * handle method, url, headers, data
        # * figure out what headers we need to automatically set (referer, ...)
        # * can we use some qt thing (page.get()?) to do ^
        # * should probably check how cookies are handled
        #   chrome/tampermonkey sends cookies (for the requested domain,
        #   duh) with GM_xhr requests
        # https://openuserjs.org/
        # https://greasyfork.org/en/scripts

        # tampermoney on chrome prompts when a script tries to do a
        # cross-origin request.
        print("==============================================")
        print("GM_xmlhttpRequest")
        print(details)

        if not 'url' in details:
            return

        request_index = details['_qute_gm_request_index']
        if not request_index:
            log.greasemonkey.error(("GM_xmlhttpRequest received request "
                                    "without nonce, skipping."))
            return

        if objreg.get('host-blocker').is_blocked(QUrl(details['url'])):
            return

        # TODO: url might be relative, need to fix on the JS side.
        request = QNetworkRequest(QUrl(details['url']))
        request.setOriginatingObject(self)
        # The C++ docs say the default is to not follow any redirects.
        request.setAttribute(QNetworkRequest.RedirectionTargetAttribute,
                             QNetworkRequest.NoLessSafeRedirectPolicy)
        # TODO: Ensure these headers are encoded to spec if containing eg
        # unicodes
        if 'headers' in details:
            for k, v in details['headers'].items():
                # With this script: https://raw.githubusercontent.com/evazion/translate-pixiv-tags/master/translate-pixiv-tags.user.js
                # One of the headers it 'X-Twitter-Polling': True, which was
                # causing the below to error out because v is a bool. Not sure
                # where that is coming from or what value twitter expects.
                # That script is patching jquery so try with unpatched jquery
                # and see what it does.
                request.setRawHeader(k.encode('ascii'), str(v).encode('ascii'))

        # TODO: Should we allow xhr to set user-agent?
        if not request.header(QNetworkRequest.UserAgentHeader):
            request.setHeader(QNetworkRequest.UserAgentHeader,
                              self.profile.httpUserAgent())

        payload = details.get('data', None)
        if payload:
            # Should check encoding from content-type header?
            payload = payload.encode('utf-8')

        reply = self.nam.sendCustomRequest(
            request,
            details.get('method', 'GET').encode('ascii'), payload)

        if reply.isFinished():
            self.handle_xhr_reply(reply, request_index)
        else:
            reply.finished.connect(
                functools.partial(self.handle_xhr_reply, reply, request_index))