def get_http_auth_credentials(qurl, authenticator, parent=None):
    qurl = QUrl(qurl)
    qurl.setFragment(None)
    realm = authenticator.realm()
    trealm = (' (%s)' % realm) if realm else ''
    ac = parent and parent.get_login_credentials(qurl.toString())
    d = Credentials(_('Please specify a password for {0}{1}').format(qurl.toString(), trealm), parent)
    if ac is not None:
        if ac['autologin']:
            authenticator.setUser(ac['username'])
            authenticator.setPassword(ac['password'])
            return
        d.credentials = ac['username'], ac['password']

    if d.exec() == QDialog.DialogCode.Accepted:
        username, password = d.credentials
        authenticator.setUser(username)
        authenticator.setPassword(password)
        if parent is not None:
            parent.on_login_form_submit(qurl.toString(), username, password)
    else:
        if parent is not None:
            parent.setHtml('<p style="font-family:sans-serif">{} {}</p>'.format(
                _('Authentication required to access: '), '<a href="{0}">{0}</a>'.format(qurl.toDisplayString()))) def get_proxy_auth_credentials(qurl, authenticator, proxy_host, parent=None):
    qurl = QUrl(qurl)
    qurl.setFragment(None)
    d = Credentials(_('Please specify a password for {0} at the proxy: {1}').format(qurl.toString(), proxy_host), parent)
    if d.exec() == d.Accepted:
        username, password = d.credentials
        authenticator.setUser(username)
        authenticator.setPassword(password) class Ask(Dialog):
class CertExceptions:
    def __init__(self, msg, parent=None):
    def setup_ui(self):
    def permanent_toggled(self):
    def sizeHint(self):
    def __init__(self):
    def conn(self):
    def add_exception(self, domain, etype, permanent=True):
    def has_exception(self, domain, etype):
    def show_error(self, domain, error_string, parent=None):
    def ask(self, odomain, code, error_string, parent=None): def connect_signal(name=None, func_name=None):
    def connect(f):
        fname = func_name or f.__name__
        n = name or fname
        if n in from_js:
            raise KeyError('A signal with the name of %s has already been connected' % name)

        from_js[n] = fname
        return f
    return connect def js_to_python(page, name, args):
    func_name = from_js.get(name)
    if func_name is None:
        print('Unknown signal received from js:', name)
        return
    func = getattr(page, func_name, None)
    if func is None:
        func = getattr(page.parent(), func_name, None)
    if func is None:
        print('Unknown signal received from js:', name)
        return
    func = getattr(func, 'emit', func)
    func(*args) def python_to_js(page_or_tab, name, *args):
    page = page_or_tab.page() if isinstance(page_or_tab, QWebEngineView) else page_or_tab
    page.runJavaScript('window.send_message_to_javascript(%s, %s)' % (json.dumps(name), json.dumps(args)), QWebEngineScript.ScriptWorldId.ApplicationWorld) @lru_cache()
def misc_config(key, default=None):
    u = load_config(user=True).get(key) or load_config(user=False).get(key)
    if not u or u == 'default':
        u = default
    return u FOLLOW_LINK_KEY_MAP = {getattr(Qt.Key, 'Key_' + x.upper()): x for x in string.ascii_lowercase + string.digits} class DevTools(QWebEngineView):

    def __init__(self, parent=None):
        QWebEngineView.__init__(self, parent)

    def set_inspected_view(self, view=None):
        self.page().setInspectedPage(view.page() if view else None)

    def sizeHint(self):
        return default_size_hint(QWebEngineView.sizeHint(self)) def get_download_dir():
    if not hasattr(get_download_dir, 'ans'):
        get_download_dir.ans = os.path.expanduser(misc_config('download_dir', default='~/downloads'))
        try:
            os.makedirs(get_download_dir.ans)
        except FileExistsError:
            pass
    return get_download_dir.ans def question_dialog(
        parent, title, msg, det_msg='', show_copy_button=False,
        default_yes=True,
        # Skippable dialogs
        # Set skip_dialog_name to a unique name for this dialog
        # Set skip_dialog_msg to a message displayed to the user
        skip_dialog_name=None, skip_dialog_msg=_('Show this confirmation again'),
        skip_dialog_skipped_value=True, skip_dialog_skip_precheck=True,
        # Override icon (QIcon to be used as the icon for this dialog or string for I())
        override_icon=None,
        # Change the text/icons of the yes and no buttons.
        # The icons must be QIcon objects or strings for I()
        yes_text=None, no_text=None, yes_icon=None, no_icon=None,
):

    auto_skip = set(gprefs.get('questions_to_auto_skip', []))
    if (skip_dialog_name is not None and skip_dialog_name in auto_skip):
        return bool(skip_dialog_skipped_value)

    d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
                   show_copy_button=show_copy_button, default_yes=default_yes,
                   q_icon=override_icon, yes_text=yes_text, no_text=no_text,
                   yes_icon=yes_icon, no_icon=no_icon)

    if skip_dialog_name is not None and skip_dialog_msg:
        tc = d.toggle_checkbox
        tc.setVisible(True)
        tc.setText(skip_dialog_msg)
        tc.setChecked(bool(skip_dialog_skip_precheck))
        d.resize_needed.emit()

    ret = d.exec() == QDialog.DialogCode.Accepted

    if skip_dialog_name is not None and not d.toggle_checkbox.isChecked():
        auto_skip.add(skip_dialog_name)
        gprefs.set('questions_to_auto_skip', list(auto_skip))

    return ret class PasswordWrong(ValueError):
class PasswordStore:  # {{{
class PasswordDB:
class DelayLoadedPasswordDB(PasswordDB):
    def has_password(self, dirpath=None):
    def __init__(self, password, dirpath=None, pw_is_key=False):
    def run_derive(self):
    def commit_metadata(self):
    def derive_key(self):
    def generate_file_name(self, key):
    def join(self):
    def read_data(self, fname, key=None):
    def __iter__(self):
    def __contains__(self, key):
    def get_data(self, key):
    def get_all_data(self):
    def set_data(self, key, data=None):
    def change_password(self, new_password):
def test():
def key_from_url(url):
    def has_password(cls, path=None):
    def __init__(self, password, path=None, pw_is_key=False):
    def __getitem__(self, key):
    def __setitem__(self, key, val):
    def __delitem__(self, key):
    def __contains__(self, key):
    def __iter__(self):
    def get_accounts(self, key):
    def add_account(self, key, username, password, notes=None, autologin=None):
    def remove_account(self, key, username):
    def set_accounts(self, key, accounts):
    def change_password(self, new_password):
    def __init__(self):
    def start_load(self, password, callback=None, pw_is_key=False):
        def loadpw():
    def join(self):
    def is_loaded(self):
    def key(self):
    def key_error(self):
def import_lastpass_db(path, db): def now():
def normalize(x):
    def __init__(self, path=None):
    def conn(self):
    def insert(self, table, cursor=None, **kw):
    def on_visit(self, qurl, visit_type, is_main_frame):
    def merge_places(self, src_place_id, dest_place_id):
        def data(place_id):
    def merge_https_places(self, http_qurl=None):
            def place_id_for(url):
    def transform_urls(self, transform_func=None):
    def calculate_frecency(self, place_id, visit_count, cursor=None):
    def on_title_change(self, qurl, title):
    def on_favicon_change(self, qurl, favicon_qurl):
    def prune(self, days=400):
    def close(self):
    def subsequence_matches(self, subsequence=None, limit=50):
    def substring_matches(self, substrings=None, limit=50):
    def favicon_url(self, place_id):
def import_from_firefox():
DAY = int(24 * 60 * 60 * 1e6)
FRECENCY_NUM_VISITS = 10
VISIT_TYPE_WEIGHTS = {QWebEnginePage.NavigationType.NavigationTypeLinkClicked.value: 120, QWebEnginePage.NavigationType.NavigationTypeTyped.value: 200}
RECENCY_WEIGHTS = [100, 70, 50, 30, 10]
class Places: class Popup(QWidget):

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        parent.resized.connect(self.parent_resized, type=Qt.ConnectionType.QueuedConnection)
        self.questions = []
        self.move(0, 1)
        self.hide()
        self.l = l = QHBoxLayout(self)
        l.setStretch(0, 100)
        self.msg = msg = QLabel('\xa0')
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, self)
        l.addWidget(msg), l.addWidget(bb)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.question_id = 0
        self.shutting_down = False
        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        bb.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.msg.setFocusPolicy(Qt.FocusPolicy.NoFocus)

    def accept(self):
        self.finish(True)

    def reject(self):
        self.finish(False)

    def parent_resized(self):
        self.resize(self.parent().width(), self.sizeHint().height())

    def ask(self, text, callback=None, extra_buttons=None):
        self.question_id += 1
        self.questions.append(Question(self.question_id, text, callback, extra_buttons or {}))
        self.show_question()
        return self.question_id
    __call__ = ask

    def abort_question(self, question_id):
        if self.isVisible() and self.questions and self.questions[0].id == question_id:
            self.questions.pop(0)
            self.hide()
            return
        for i, question in enumerate(self.questions):
            if question.id == question_id:
                break
        else:
            return
        del self.questions[i]

    def show_question(self):
        if not self.questions:
            return
        q = self.questions[0]
        self.msg.setText(q.text or '')
        self.bb.clear()
        if q.callback is None:
            self.bb.setStandardButtons(QDialogButtonBox.StandardButton.Close)
        else:
            self.bb.setStandardButtons(QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No)
        for text, val in q.extra_buttons.items():
            b = self.bb.addButton(text, QDialogButtonBox.ButtonRole.AcceptRole)
            if q.callback is not None:
                b.clicked.connect(partial(self.extra_button_callback, q.callback, val))
            b.setObjectName(val)
        self.show()

    def extra_button_callback(self, callback, val):
        callback(val, self.shutting_down)

    def show(self):
        self.move(0, 1)
        self.parent_resized()
        QWidget.show(self)
        self.raise_()

    def break_cycles(self):
        while self.questions:
            self.finish(False)

    def finish(self, accepted):
        if self.questions:
            q = self.questions.pop(0)
            if q.callback is not None:
                q.callback(accepted, self.shutting_down)
        if self.questions:
            self.show_question()
        else:
            self.hide()

    def paintEvent(self, ev):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
        painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True)
        try:
            self.paint_background(painter)
        except Exception:
            pass
        finally:
            painter.end()
        QWidget.paintEvent(self, ev)

    def paint_background(self, painter):
        br = 12  # border_radius
        bw = 1  # border_width
        c = QColor('#fdfd96')
        p = QPainterPath()
        p.addRoundedRect(QRectF(self.rect()), br, br)
        painter.fillPath(p, c)
        p.addRoundedRect(QRectF(self.rect()).adjusted(bw, bw, -bw, -bw), br, br)
        painter.fillPath(p, QColor('black')) def to_json(obj):
def from_json(obj):
    def __init__(self, name):
    def conn(self):
    def get(self, name, default=None):
    def set(self, name, val):
    def __getitem__(self, name):
    def __setitem__(self, name, val):
    def __delitem__(self, name):
    def buffer_commits(self):
    def buffer_commits(self, val):
    def __enter__(self):
    def __exit__(self, *args):
def safe_makedirs(path):
def create_script(
    name, src, world=QWebEngineScript.ScriptWorldId.ApplicationWorld, injection_point=QWebEngineScript.InjectionPoint.DocumentCreation, on_subframes=True
):
def client_script():
def insert_scripts(profile, *scripts):
def get_spell_langs():
def create_profile(parent=None, private=False):
def profile():
def do_delete_profile(profile):
def delete_profile():
def quickmarks():
class DynamicPrefs:
TITLE_TOKEN = None
    TITLE_TOKEN = hexlify(os.urandom(32)).decode('ascii') class Permissions:
    def __init__(self):
    def conn(self):
    def has_permission(self, qurl_or_domain, permission_type):
    def add_permission(self, qurl_or_domain, permission_type, permanent=True): class Dialog(QDialog):

    '''
    An improved version of Qt's QDialog class. This automatically remembers the
    last used size, automatically connects the signals for QDialogButtonBox,
    automatically sets the window title and if the dialog has an object named
    splitter, automatically saves the splitter state.

    In order to use it, simply subclass an implement setup_ui(). You can also
    implement sizeHint() to give the dialog a different default size when shown
    for the first time.
    '''

    def __init__(self, title, name, parent=None, prefs=gprefs):
        QDialog.__init__(self, parent)
        self.prefs_for_persistence = prefs
        self.setWindowTitle(title)
        self.name = name
        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)

        self.setup_ui()

        self.resize(self.sizeHint())
        geom = self.prefs_for_persistence.get(name + '-geometry', None)
        if geom is not None:
            self.restoreGeometry(geom)
        if hasattr(self, 'splitter'):
            state = self.prefs_for_persistence.get(name + '-splitter-state', None)
            if state is not None:
                self.splitter.restoreState(state)

    def accept(self):
        self.prefs_for_persistence.set(self.name + '-geometry', bytearray(self.saveGeometry()))
        if hasattr(self, 'splitter'):
            self.prefs_for_persistence.set(self.name + '-splitter-state', bytearray(self.splitter.saveState()))
        QDialog.accept(self)

    def reject(self):
        self.prefs_for_persistence.set(self.name + '-geometry', bytearray(self.saveGeometry()))
        if hasattr(self, 'splitter'):
            self.prefs_for_persistence.set(self.name + '-splitter-state', bytearray(self.splitter.saveState()))
        QDialog.reject(self)

    def setup_ui(self):
        raise NotImplementedError('You must implement this method in Dialog subclasses') def ascii_lowercase(val):
    return re.sub('[%s]' % string.ascii_uppercase, lambda m: m.group().lower(), val) def icon_to_data(icon, w=64, h=64):
    if icon.isNull():
        return b''
    if w is None:
        sizes = sorted(icon.availableSizes(), key=lambda x: x.width() * x.height())
        p = icon.pixmap(sizes[-1])
    else:
        p = icon.pixmap(w, h)
    if p.isNull():
        return b''
    return pixmap_to_data(p) def open_local_file(path):
    QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def safe_disconnect(signal):
    try:
        signal.disconnect()
    except TypeError:
        pass  # signal was not connected
import json
import os
import shlex
import subprocess
import weakref
            import traceback
                import traceback
from base64 import standard_b64encode
from functools import partial
from gettext import gettext as _
from itertools import count
from tempfile import NamedTemporaryFile
from threading import Thread
from time import monotonic
from PyQt6 import sip
from PyQt6.QtCore import QMarginsF, QSize, Qt, QUrl, pyqtSignal, QEvent
from PyQt6.QtGui import QKeyEvent, QPageLayout, QPageSize
from PyQt6.QtWebEngineCore import QWebEnginePage, QWebEngineScript
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QApplication, QCheckBox, QGridLayout, QLabel, QDialogButtonBox
from .auth import get_http_auth_credentials, get_proxy_auth_credentials
from .certs import cert_exceptions
from .communicate import connect_signal, js_to_python, python_to_js
from .config import misc_config
from .constants import FOLLOW_LINK_KEY_MAP
from .dev_tools import DevTools
from .downloads import get_download_dir
from .message_box import question_dialog
from .passwd.db import key_from_url, password_db, password_exclusions
from .places import places
from .popup import Popup
from .settings import gprefs
from .site_permissions import site_permissions
from .utils import (Dialog, ascii_lowercase, icon_to_data, open_local_file,
                    safe_disconnect)
            from .message_box import error_dialog
            from .message_box import error_dialog
        from .settings import TITLE_TOKEN
        from .downloads import save_page_path_map
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>




view_id = count()
certificate_error_domains = set()


class Alert(Dialog):  # {{{

    suppressed_alerts = set()

    def __init__(self, title, qurl, msg, parent):
        title = title or qurl.host() or qurl.toString()
        self.msg = msg
        self.key = qurl.toString()
        Dialog.__init__(self, _('Alert from') + ': ' + title, 'alert', parent)

    def setup_ui(self):
        self.lay = lay = QGridLayout(self)
        self.la = la = QLabel(self.msg)
        la.setWordWrap(True)
        lay.addWidget(la, 0, 0, 1, -1)
        self.setMaximumWidth(self.parent().width())
        self.setMaximumHeight(self.parent().height())
        self.cb = cb = QCheckBox(_('&Suppress future alerts from this site'), self)
        cb.toggled.connect(self.suppress_toggled)
        lay.addWidget(cb, 1, 0)
        lay.addWidget(self.bb, 1, 1), self.bb.setStandardButtons(QDialogButtonBox.StandardButton.Close)

    def suppress_toggled(self):
        if self.cb.isChecked():
            Alert.suppressed_alerts.add(self.key)

    def sizeHint(self):
        ans = Dialog.sizeHint(self)
        ans.setWidth(min(self.maximumWidth(), ans.width() + 150))
        return ans
# }}}


def edit_text(viewref, text, frame_id, eid):  # {{{
    defedit = os.environ.get('VISUAL', os.environ.get('EDITOR', 'vim'))
    defedit = 'kitty ' + defedit
    editor = shlex.split(misc_config('editor', default=defedit))
    with NamedTemporaryFile(prefix='vise-edit-file-', suffix='.txt', delete=False) as f:
        f.write(text.encode('utf-8'))
    try:
        ret = subprocess.Popen(editor + [f.name]).wait()
        if ret == 0:
            with open(f.name, 'rb') as f:
                new_text = f.read().decode('utf-8')
            if new_text != text:
                view = viewref()
                if view is not None:
                    view.set_editable_text_in_gui_thread.emit(new_text, frame_id, eid)
    finally:
        os.remove(f.name)
# }}}


class WebPage(QWebEnginePage):

    poll_for_messages = pyqtSignal()

    def __init__(self, profile, parent):
        QWebEnginePage.__init__(self, profile, parent)
        self.authenticationRequired.connect(self.authentication_required)
        self.proxyAuthenticationRequired.connect(self.proxy_authentication_required)
        self.callbacks = {'vise_downloads_page': (self.downloads_callback, (), {})}
        self.poll_for_messages.connect(self.check_for_messages_from_js, type=Qt.ConnectionType.QueuedConnection)
        self.certificateError.connect(self.on_certificate_error)

    def register_callback(self, name, func, *args, **kw):
        self.callbacks[name] = (func, args, kw)

    def downloads_callback(self, *args, **kw):
        return QApplication.instance().downloads.callback(*args, **kw)

    def check_for_messages_from_js(self):
        self.runJavaScript('try { window.get_messages_from_javascript() } catch(TypeError) {}',
                           QWebEngineScript.ScriptWorldId.ApplicationWorld, self.messages_received_from_js)

    def messages_received_from_js(self, messages):
        if messages and messages != '[]':
            for msg in json.loads(messages):
                mtype = msg['type']
                if mtype == 'callback':
                    self.called_back(msg['name'], msg['data'])
                elif mtype == 'js_to_python':

                    js_to_python(self, msg['name'], msg['args'])