Warning, /plasma/drkonqi/src/qml/DeveloperPage.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
0003 
0004 import QtQuick 2.15
0005 import QtQuick.Layouts 1.15
0006 import QtQuick.Controls 2.15 as QQC2
0007 import org.kde.kirigami 2.19 as Kirigami
0008 import org.kde.syntaxhighlighting 1.0
0009 
0010 import org.kde.drkonqi 1.0
0011 
0012 Kirigami.ScrollablePage {
0013     id: page
0014 
0015     property alias reportActionVisible: reportAction.visible
0016     property string trace: ""
0017     property bool basic: false
0018     property alias usefulness: ratingItem.usefulness
0019     property alias footerActionsLeft: footerBarLeft.actions
0020     property alias footerActionsRight: footerBarRight.actions
0021 
0022     padding: 0
0023     bottomPadding: 0
0024 
0025     title: i18nc("@title:window", "Developer Information")
0026 
0027     actions: [
0028         Kirigami.Action {
0029             id: reportAction
0030             enabled: Kirigami.Settings.isMobile ? true : canReport
0031             visible: Kirigami.Settings.isMobile ? canReport : true
0032             icon.name: "story-editor-symbolic"
0033             text: i18nc("@action Report the bug on this domain", "Report on %1", Globals.bugzillaShortUrl)
0034             // TODO: could give context on why the button is disabled when canReport is false
0035             tooltip: i18nc("@info:tooltip", "Starts the bug report assistant.")
0036             onTriggered: pageStack.push("qrc:/ui/WelcomePage.qml")
0037         },
0038 
0039         Kirigami.Action {
0040             id: installButton
0041             visible: false
0042             text: i18nc("@action:button", "Install Debug Symbols")
0043             onTriggered: {
0044                 if (debugPackageInstaller.canInstallDebugPackages) { // prefer the installer when available over dynamic resolution
0045                     debugPackageInstaller.installDebugPackages()
0046                 } else if (BacktraceGenerator.supportsSymbolResolution) {
0047                     traceArea.text = ""
0048                     BacktraceGenerator.symbolResolution = true
0049                     BacktraceGenerator.start()
0050                 } else {
0051                     console.warn("Unexpected install button state :O")
0052                 }
0053             }
0054             icon.name: "install"
0055             tooltip: i18nc("@info:tooltip", "Use this button to install the missing debug symbols packages.")
0056         },
0057 
0058         Kirigami.Action {
0059             id: reloadAction
0060             enabled: BacktraceGenerator.state !== BacktraceGenerator.Loading
0061             icon.name: "view-refresh"
0062             text: i18nc("@action:button", "Reload")
0063             tooltip: xi18nc("@info:tooltip",
0064 `Use this button to reload the crash information (backtrace). This is useful when you have
0065 installed the proper debug symbol packages and you want to obtain a better backtrace.`)
0066             onTriggered: {
0067                 traceArea.text = ""
0068                 BacktraceGenerator.start()
0069             }
0070         },
0071 
0072         Kirigami.Action {
0073             icon.name: "edit-copy"
0074             text: i18nc("@action:button", "Copy")
0075             tooltip: i18nc("@info:tooltip", "Use this button to copy the crash information (backtrace) to the clipboard.")
0076             onTriggered: DrKonqi.copyToClipboard(traceArea.text)
0077         },
0078 
0079         Kirigami.Action {
0080             icon.name: "document-save"
0081             text: i18nc("@action:button", "Save")
0082             tooltip: xi18nc("@info:tooltip",
0083 `Use this button to save the crash information (backtrace) to a file. This is useful if you want to take a look at it or to report the bug later.`)
0084             onTriggered: DrKonqi.saveReport(traceArea.text)
0085         }
0086     ]
0087 
0088     header: QQC2.ToolBar {
0089         RatingItem {
0090             id: ratingItem
0091             anchors.fill: parent
0092             failed: BacktraceGenerator.state === BacktraceGenerator.Failed || BacktraceGenerator.state === BacktraceGenerator.FailedToStart
0093             loading: BacktraceGenerator.state === BacktraceGenerator.Loading
0094         }
0095     }
0096 
0097     ColumnLayout {
0098         spacing: 0
0099 
0100         DebugPackageInstaller { // not in global scope because it messes up scrollbars
0101             id: debugPackageInstaller
0102             onPackagesInstalled: reloadAction.trigger()
0103             onError: appWindow.showPassiveNotification(i18nc("@title:window", "Error during the installation of debug symbols"), "long")
0104         }
0105 
0106         RowLayout {
0107             visible: page.basic
0108             spacing: Kirigami.Units.smallSpacing
0109             Layout.fillHeight: true
0110             Kirigami.Icon {
0111                 source: "help-hint"
0112                 width: Kirigami.Units.iconSizes.enormous
0113                 height: width
0114             }
0115             QQC2.Label {
0116                 Layout.fillHeight: true
0117                 Layout.fillWidth: true
0118                 wrapMode: Text.Wrap
0119                 text: xi18nc("@info",
0120 `<subtitle>What is a "backtrace" ?</subtitle><para>A backtrace basically describes what was
0121 happening inside the application when it crashed, so the developers may track
0122 down where the mess started. They may look meaningless to you, but they might
0123 actually contain a wealth of useful information.<nl />Backtraces are commonly
0124 used during interactive and post-mortem debugging.</para>`)
0125             }
0126         }
0127         QQC2.TextArea {
0128             id: traceArea
0129             Layout.fillWidth: true
0130             Layout.fillHeight: true
0131             visible: text !== "" && !page.basic
0132 
0133             // text: output.text
0134             font.family: "monospace"
0135             wrapMode: TextEdit.Wrap
0136             textFormat: TextEdit.PlainText
0137             readOnly: true
0138             selectByMouse: Kirigami.Settings.isMobile ? false : true
0139 
0140             SyntaxHighlighter {
0141                 textEdit:  BacktraceGenerator.debuggerIsGDB() ? traceArea : undefined
0142                 definition: "GDB Backtrace"
0143             }
0144 
0145             function ensureVisible(r) {
0146                 if (flickable.contentX >= r.x) {
0147                     flickable.contentX = r.x;
0148                 } else if (flickable.contentX + flickable.width <= r.x + r.width) {
0149                     flickable.contentX = r.x + r.width - flickable.width;
0150                 }
0151                 if (flickable.contentY >= r.y) {
0152                     flickable.contentY = r.y;
0153                 } else if (flickable.contentY + flickable.height <= r.y + r.height) {
0154                     flickable.contentY = r.y + r.height - flickable.height;
0155                 }
0156             }
0157 
0158             Connections {
0159                 id: generatorConnections
0160                 target: BacktraceGenerator
0161                 function onNewLine(line) { traceArea.text += line }
0162                 function onStateChanged() {
0163                     console.log(BacktraceGenerator.state)
0164                     console.log(BacktraceGenerator.Loaded)
0165 
0166                     const state = BacktraceGenerator.state
0167                     page.state = state
0168 
0169                     installButton.visible = false
0170 
0171                     const parser = BacktraceGenerator.parser();
0172                     usefulness = parser.backtraceUsefulness()
0173                     // ratingItem.usefulness = usefulness
0174                     if (state == BacktraceGenerator.Loaded) {
0175                         traceArea.text = BacktraceGenerator.backtrace()
0176                         // Kinda hacky. Scroll all the way down, then scroll up until the handler is visible.
0177                         // This should bring the most relevant frames into the viewport.
0178                         traceArea.cursorPosition = traceArea.length - 1
0179                         traceArea.ensureVisible(traceArea.cursorRectangle)
0180                         traceArea.cursorPosition = traceArea.text.indexOf("[KCrash Handler]")
0181                         traceArea.ensureVisible(traceArea.cursorRectangle)
0182                         trace = traceArea.text // FIXME ensure this doesn't result in a binding
0183 
0184                         if (usefulness != BacktraceParser.ReallyUseful) {
0185                             if (debugPackageInstaller.canInstallDebugPackages || BacktraceGenerator.supportsSymbolResolution) {
0186                                 detailsLabel.text = xi18nc("@info/rich",
0187 `You can click the <interface>Install Debug Symbols</interface> button in order to automatically install the missing debugging information packages. If this method
0188 does not work: please read <link url='%1'>How to create useful crash reports</link> to learn how to get a useful
0189 backtrace; install the needed packages (<link url='%2'>list of files</link>) and click the <interface>Reload</interface> button.`,
0190                                                             Globals.techbaseHowtoDoc, '#missingDebugPackages')
0191                                 installButton.visible = true
0192                                 debugPackageInstaller.setMissingLibraries(parser.librariesWithMissingDebugSymbols())
0193                             } else {
0194                                 detailsLabel.text = xi18nc("@info/rich",
0195 `Please read <link url='%1'>How to create useful crash reports</link> to learn how to get a useful backtrace; install the needed packages
0196 (<link url='%2'>list of files</link>) and click the <interface>Reload</interface> button.`,
0197                                                             Globals.techbaseHowtoDoc, '#missingDebugPackages')
0198                             }
0199                         }
0200                     } else if (state == BacktraceGenerator.Failed) {
0201                         traceArea.text = i18nc("@info:status", "The crash information could not be generated.")
0202                         detailsLabel.text = xi18nc("@info/rich", `You could try to regenerate the backtrace by clicking the <interface>Reload</interface> button.`)
0203                     } else if (state == BacktraceGenerator.FailedToStart) {
0204                         // FIXME dupe from failed
0205                         traceArea.text = i18nc("@info:status", "The crash information could not be generated.")
0206                         detailsLabel.text = xi18nc("@info/rich",
0207 `<emphasis strong='true'>You need to first install the debugger application (%1) then click the <interface>Reload</interface> button.</emphasis>`,
0208                                                    BacktraceGenerator.debuggerName())
0209                     }
0210                 }
0211             }
0212         }
0213     }
0214 
0215     footer: ColumnLayout {
0216         QQC2.Control { // Get standard padding so it doesn't stick to the edges, Label has none by default cause it isn't a Control
0217             Layout.fillWidth: true
0218             background: null
0219 
0220             Kirigami.PromptDialog {
0221                 id: filesDialog
0222                 title: i18nc("@title", "Not Sufficiently Useful")
0223                 function reload() {
0224                     const parser = BacktraceGenerator.parser()
0225                     let missingDbgForFiles = parser.librariesWithMissingDebugSymbols()
0226                     missingDbgForFiles.unshift(CrashedApplication.exectuableAbsoluteFilePath)
0227                     // TODO should maybe prepend DrKonqi::crashedApplication()->executable().absoluteFilePath()
0228                     // NB: cannot use xi18nc here because that'd close the html tag but we need to append an unordered list of paths
0229                     let message = "<html>" + i18n("The packages containing debug information for the following application and libraries are missing:") + "<br /><ul>";
0230                     for (const i in missingDbgForFiles) {
0231                         message += "<li>" + missingDbgForFiles[i] + "</li>";
0232                     }
0233                     message += "</ul></html>"
0234                     subtitle = message
0235                 }
0236 
0237                 showCloseButton: true
0238             }
0239 
0240             contentItem: QQC2.Label {
0241                 id: detailsLabel
0242                 wrapMode: Text.Wrap
0243                 onLinkActivated: link => {
0244                     if (link[0] == "#") { // in-page reference
0245                         filesDialog.reload()
0246                         filesDialog.open()
0247                     } else {
0248                         Qt.openUrlExternally(link)
0249                     }
0250                 }
0251                 textFormat: Text.RichText
0252             }
0253         }
0254         RowLayout {
0255             spacing: Kirigami.Units.smallSpacing
0256             // Two bars because of https://bugs.kde.org/show_bug.cgi?id=451026
0257             // Awkard though, maybe we should just live with everything being right aligned
0258             FooterActionBar {
0259                 padding: 0
0260                 id: footerBarLeft
0261                 alignment: Qt.AlignLeft
0262                 visible: actions.length > 0
0263             }
0264             FooterActionBar {
0265                 padding: 0
0266                 id: footerBarRight
0267                 visible: actions.length > 0
0268             }
0269         }
0270     }
0271 
0272     Component.onCompleted: {
0273         if (BacktraceGenerator.state === BacktraceGenerator.NotLoaded) {
0274             reloadAction.trigger()
0275         } else {
0276             // ensure our current state is the state of the backend. this is important in case the developer page
0277             // was used before the report workflow (i.e. the data was generated but from a different view)
0278             generatorConnections.onStateChanged()
0279         }
0280     }
0281 }