]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/commitdiff
Implement saving of MHT web archives. https://redmine.stoutner.com/issues/1089
authorSoren Stoutner <soren@stoutner.com>
Tue, 28 Nov 2023 21:10:16 +0000 (14:10 -0700)
committerSoren Stoutner <soren@stoutner.com>
Tue, 28 Nov 2023 21:10:16 +0000 (14:10 -0700)
src/ui.rcs/browserwindowui.rc
src/widgets/TabWidget.cpp
src/widgets/TabWidget.h
src/windows/BrowserWindow.cpp

index adcfa9d0c11487b734899d5f03a7e7c60f21439b..ab3d50336cb6b41bfaa8fcb5ef0d9c9400c13bec 100644 (file)
@@ -34,6 +34,8 @@
         <Menu name="file">
             <Action name="new_tab" append="new_merge" />
             <Action name="new_window" append="new_merge" />
+
+            <Action name="save_archive" append="save_merge" />
         </Menu>
 
         <!-- View. -->
index 84cd3aca089de34bcd188e3e5baa006508254595..4a6b11c64d2bb7800497c4da9c70a970b4c0c489 100644 (file)
@@ -864,6 +864,31 @@ void TabWidget::reloadAndBypassCache() const
     currentWebEnginePagePointer->triggerAction(QWebEnginePage::ReloadAndBypassCache);
 }
 
+void TabWidget::saveArchive()
+{
+    // Get the suggested file name.
+    QString suggestedFileName = currentPrivacyWebEngineViewPointer->url().host() + ".mht";
+
+    // Get the download directory.
+    QString downloadDirectory = Settings::downloadLocation();
+
+    // Resolve the system download directory if specified.
+    if (downloadDirectory == QLatin1String("System Download Directory"))
+        downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+
+    // Get a file path from the file picker.
+    QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
+
+    // Save the webpage as an archive if the file save path is populated.
+    if (!saveFilePath.isEmpty())
+    {
+        // Set the saving archive flag.  Otherwise, a second download tries to run.
+        savingArchive = true;
+
+        // Save the archive.
+        currentWebEnginePagePointer->save(saveFilePath);
+    }
+}
 
 void TabWidget::setTabBarVisible(const bool visible) const
 {
@@ -873,163 +898,170 @@ void TabWidget::setTabBarVisible(const bool visible) const
 
 void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer)
 {
-    // Get the download attributes.
-    QUrl downloadUrl = webEngineDownloadItemPointer->url();
-    QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
-    QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
-    int totalBytes = webEngineDownloadItemPointer->totalBytes();
-
-    // Check to see if Privacy Browser is not running KDE or if local storage (cookies) is enabled.
-    if (!isRunningKde || currentPrivacyWebEngineViewPointer->localStorageEnabled)  // KDE is not running or local storage (cookies) is enabled.  Use WebEngine's downloader.
+    // Only show the save dialog if an archive is not currently being saved.  Otherwise, two save dialogs will be shown.
+    if (!savingArchive)
     {
-        // Instantiate the save dialog.
-        SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes);
+        // Get the download attributes.
+        QUrl downloadUrl = webEngineDownloadItemPointer->url();
+        QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
+        QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
+        int totalBytes = webEngineDownloadItemPointer->totalBytes();
+
+        // Check to see if Privacy Browser is not running KDE or if local storage (cookies) is enabled.
+        if (!isRunningKde || currentPrivacyWebEngineViewPointer->localStorageEnabled)  // KDE is not running or local storage (cookies) is enabled.  Use WebEngine's downloader.
+        {
+            // Instantiate the save dialog.
+            SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes);
 
-        // Display the save dialog.
-        int saveDialogResult = saveDialogPointer->exec();
+            // Display the save dialog.
+            int saveDialogResult = saveDialogPointer->exec();
 
-        // Process the save dialog results.
-        if (saveDialogResult == QDialog::Accepted)  // Save was selected.
-        {
-            // Get the download directory.
-            QString downloadDirectory = Settings::downloadLocation();
+            // Process the save dialog results.
+            if (saveDialogResult == QDialog::Accepted)  // Save was selected.
+            {
+                // Get the download directory.
+                QString downloadDirectory = Settings::downloadLocation();
 
-            // Resolve the system download directory if specified.
-            if (downloadDirectory == QLatin1String("System Download Directory"))
-                downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+                // Resolve the system download directory if specified.
+                if (downloadDirectory == QLatin1String("System Download Directory"))
+                    downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
 
-            // Get a file path from the file picker.
-            QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
+                // Get a file path from the file picker.
+                QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
 
-            // Process the save file path.
-            if (!saveFilePath.isEmpty())  // The file save path is populated.
-            {
-                // Create a save file path file info.
-                QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
+                // Process the save file path.
+                if (!saveFilePath.isEmpty())  // The file save path is populated.
+                {
+                    // Create a save file path file info.
+                    QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
 
-                // Get the canonical save path and file name.
-                QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
-                QString saveFileName = saveFilePathFileInfo.fileName();
+                    // Get the canonical save path and file name.
+                    QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
+                    QString saveFileName = saveFilePathFileInfo.fileName();
 
-                // Set the download directory and file name.
-                webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
-                webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
+                    // Set the download directory and file name.
+                    webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
+                    webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
 
-                // Create a file download notification.
-                KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
+                    // Create a file download notification.
+                    KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
 
-                // Set the notification title.
-                fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
+                    // Set the notification title.
+                    fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
 
-                // Set the notification text.
-                fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
+                    // Set the notification text.
+                    fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
 
-                // Get the download icon from the theme.
-                QIcon downloadIcon = QIcon::fromTheme(QLatin1String("download"), QIcon::fromTheme(QLatin1String("document-save")));
+                    // Get the download icon from the theme.
+                    QIcon downloadIcon = QIcon::fromTheme(QLatin1String("download"), QIcon::fromTheme(QLatin1String("document-save")));
 
-                // Set the notification icon.
-                fileDownloadNotificationPointer->setIconName(downloadIcon.name());
+                    // Set the notification icon.
+                    fileDownloadNotificationPointer->setIconName(downloadIcon.name());
 
-                // Set the action list cancel button.
-                fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
+                    // Set the action list cancel button.
+                    fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
 
-                // Prevent the notification from being autodeleted if it is closed.  Otherwise, the updates to the notification below cause a crash.
-                fileDownloadNotificationPointer->setAutoDelete(false);
+                    // Prevent the notification from being autodeleted if it is closed.  Otherwise, the updates to the notification below cause a crash.
+                    fileDownloadNotificationPointer->setAutoDelete(false);
 
-                // Handle clicks on the cancel button.
-                connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
-                {
-                    // Cancel the download.
-                    webEngineDownloadItemPointer->cancel();
+                    // Handle clicks on the cancel button.
+                    connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
+                    {
+                        // Cancel the download.
+                        webEngineDownloadItemPointer->cancel();
 
-                    // Create a file download notification.
-                    KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
+                        // Create a file download notification.
+                        KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
 
-                    // Set the notification title.
-                    canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
+                        // Set the notification title.
+                        canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
 
-                    // Set the new text.
-                    canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
+                        // Set the new text.
+                        canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
 
-                    // Set the notification icon.
-                    canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
+                        // Set the notification icon.
+                        canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
 
-                    // Display the notification.
-                    canceledDownloadNotificationPointer->sendEvent();
-                });
+                        // Display the notification.
+                        canceledDownloadNotificationPointer->sendEvent();
+                    });
 
-                // Update the notification when the download progresses.
-                connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
-                {
-                    // Set the new text.  Total bytes will be 0 if the download size is unknown.
-                    if (totalBytes > 0)
+                    // Update the notification when the download progresses.
+                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
                     {
-                        // Calculate the download percentage.
-                        int downloadPercentage = 100 * bytesReceived / totalBytes;
-
-                        // Set the file download notification text.
-                        fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
-                                                                    bytesReceived, totalBytes));
-                    }
-                    else
+                        // Set the new text.  Total bytes will be 0 if the download size is unknown.
+                        if (totalBytes > 0)
+                        {
+                            // Calculate the download percentage.
+                            int downloadPercentage = 100 * bytesReceived / totalBytes;
+
+                            // Set the file download notification text.
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
+                                                                        bytesReceived, totalBytes));
+                        }
+                        else
+                        {
+                            // Set the file download notification text.
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, bytesReceived));
+                        }
+
+                        // Display the updated notification.
+                        fileDownloadNotificationPointer->update();
+                    });
+
+                    // Update the notification when the download finishes.  The save file name must be copied into the lambda or a crash occurs.
+                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
                     {
-                        // Set the file download notification text.
-                        fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, bytesReceived));
-                    }
+                        // Set the new text.
+                        fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
 
-                    // Display the updated notification.
-                    fileDownloadNotificationPointer->update();
-                });
-
-                // Update the notification when the download finishes.  The save file name must be copied into the lambda or a crash occurs.
-                connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
-                {
-                    // Set the new text.
-                    fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
+                        // Set the URL so the file options will be displayed.
+                        fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
 
-                    // Set the URL so the file options will be displayed.
-                    fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
+                        // Remove the actions from the notification.
+                        fileDownloadNotificationPointer->setActions(QStringList());
 
-                    // Remove the actions from the notification.
-                    fileDownloadNotificationPointer->setActions(QStringList());
+                        // Set the notification to disappear after a timeout.
+                        fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
 
-                    // Set the notification to disappear after a timeout.
-                    fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
+                        // Display the updated notification.
+                        fileDownloadNotificationPointer->update();
+                    });
 
-                    // Display the updated notification.
-                    fileDownloadNotificationPointer->update();
-                });
-
-                // Display the notification.
-                fileDownloadNotificationPointer->sendEvent();
+                    // Display the notification.
+                    fileDownloadNotificationPointer->sendEvent();
 
-                // Start the download.
-                webEngineDownloadItemPointer->accept();
+                    // Start the download.
+                    webEngineDownloadItemPointer->accept();
+                }
+                else  // The file save path is not populated.
+                {
+                    // Cancel the download.
+                    webEngineDownloadItemPointer->cancel();
+                }
             }
-            else  // The file save path is not populated.
+            else  // Cancel was selected.
             {
                 // Cancel the download.
                 webEngineDownloadItemPointer->cancel();
             }
         }
-        else  // Cancel was selected.
+        else  // KDE is running and local storage (cookies) is disabled.  Use KDE's native downloader.
+            // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
         {
-            // Cancel the download.
-            webEngineDownloadItemPointer->cancel();
-        }
-    }
-    else  // KDE is running and local storage (cookies) is disabled.  Use KDE's native downloader.
-          // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
-    {
-        // Instantiate the save dialog.  `true` instructs it to use the native downloader
-        SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
+            // Instantiate the save dialog.  `true` instructs it to use the native downloader
+            SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
 
-        // Connect the save button.
-        connect(saveDialogPointer, SIGNAL(useNativeKdeDownloader(QUrl &, QString &)), this, SLOT(useNativeKdeDownloader(QUrl &, QString &)));
+            // Connect the save button.
+            connect(saveDialogPointer, SIGNAL(useNativeKdeDownloader(QUrl &, QString &)), this, SLOT(useNativeKdeDownloader(QUrl &, QString &)));
 
-        // Show the dialog.
-        saveDialogPointer->show();
+            // Show the dialog.
+            saveDialogPointer->show();
+        }
     }
+
+    // Reset the saving archive flag.
+    savingArchive = false;
 }
 
 void TabWidget::toggleDomStorage() const
index 41ba64c62f9e895707c096c2a185a992be2b2b15..ab6167188c8d09d4949c4574213c03c381c0e435 100644 (file)
@@ -116,6 +116,7 @@ public Q_SLOTS:
     void printPreview() const;
     void refresh() const;
     void reloadAndBypassCache() const;
+    void saveArchive();
 
 private Q_SLOTS:
     // The private slots.
@@ -142,8 +143,9 @@ private:
     QIcon defaultFavoriteIcon = QIcon::fromTheme(QLatin1String("globe"), QIcon::fromTheme(QLatin1String("applications-internet")));
     bool isRunningKde = false;
     QMovie *loadingFavoriteIconMoviePointer;
-    QString searchEngineUrl;
     QTabWidget *qTabWidgetPointer;
+    bool savingArchive;
+    QString searchEngineUrl;
     UserAgentHelper *userAgentHelperPointer;
     bool wipingCurrentFindTextSelection = false;
 };
index b90f132d3704e399b8f3b78d9299b62d13f91220..214b0af6888b16801986eef1189bd1363b93ffa3 100644 (file)
@@ -91,6 +91,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     // Add the custom actions.
     QAction *newTabActionPointer = actionCollectionPointer->addAction(QLatin1String("new_tab"));
     QAction *newWindowActionPointer = actionCollectionPointer->addAction(QLatin1String("new_window"));
+    QAction *saveArchiveActionPointer = actionCollectionPointer->addAction(QLatin1String("save_archive"));
     zoomDefaultActionPointer = actionCollectionPointer->addAction(QLatin1String("zoom_default"));
     QAction *reloadAndBypassCacheActionPointer = actionCollectionPointer->addAction(QLatin1String("reload_and_bypass_cache"));
     viewSourceActionPointer = actionCollectionPointer->addAction(QLatin1String("view_source"));
@@ -171,12 +172,13 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     UserAgentHelper *userAgentHelperPointer = new UserAgentHelper();
 
     // Set the action text.
-    viewSourceActionPointer->setText(i18nc("View source action", "View Source"));
-    viewSourceInNewTabActionPointer->setText(i18nc("View source in new tab action", "View Source in New Tab"));
     newTabActionPointer->setText(i18nc("New tab action", "New Tab"));
     newWindowActionPointer->setText(i18nc("New window action", "New Window"));
+    saveArchiveActionPointer->setText(i18nc("Save archive action", "Save Archive"));
     zoomDefaultActionPointer->setText(i18nc("Zoom default action", "Zoom Default"));
     reloadAndBypassCacheActionPointer->setText(i18nc("Reload and bypass cache action", "Reload and Bypass Cache"));
+    viewSourceActionPointer->setText(i18nc("View source action", "View Source"));
+    viewSourceInNewTabActionPointer->setText(i18nc("View source in new tab action", "View Source in New Tab"));
     javaScriptActionPointer->setText(i18nc("JavaScript action", "JavaScript"));
     localStorageActionPointer->setText(i18nc("The Local Storage action", "Local Storage"));
     domStorageActionPointer->setText(i18nc("DOM Storage action", "DOM Storage"));
@@ -204,6 +206,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     // The toolbar icons don't pick up unless the size is explicit, probably because the toolbar ends up being an intermediate size.
     newTabActionPointer->setIcon(QIcon::fromTheme(QLatin1String("tab-new")));
     newWindowActionPointer->setIcon(QIcon::fromTheme(QLatin1String("window-new")));
+    saveArchiveActionPointer->setIcon(QIcon::fromTheme(QLatin1String("document-save")));
     zoomDefaultActionPointer->setIcon(QIcon::fromTheme(QLatin1String("zoom-fit-best")));
     reloadAndBypassCacheActionPointer->setIcon(QIcon::fromTheme(QLatin1String("view-refresh")));
     viewSourceActionPointer->setIcon(QIcon::fromTheme(QLatin1String("view-choose"), QIcon::fromTheme(QLatin1String("accessories-text-editor"))));
@@ -238,6 +241,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     // Create the key sequences.
     QKeySequence ctrlTKeySequence = QKeySequence(i18nc("The open new tab key sequence.", "Ctrl+T"));
     QKeySequence ctrlNKeySequence = QKeySequence(i18nc("The open new window key sequence.", "Ctrl+N"));
+    QKeySequence ctrlAKeySequence = QKeySequence(i18nc("The save archive key sequence.", "Ctrl+A"));
     QKeySequence ctrl0KeySequence = QKeySequence(i18nc("The zoom default key sequence.", "Ctrl+0"));
     QKeySequence ctrlF5KeySequence = QKeySequence(i18nc("The reload and bypass cache key sequence.", "Ctrl+F5"));
     QKeySequence ctrlUKeySequence = QKeySequence(i18nc("The view source key sequence.", "Ctrl+U"));
@@ -272,6 +276,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     // Set the action key sequences.
     actionCollectionPointer->setDefaultShortcut(newTabActionPointer, ctrlTKeySequence);
     actionCollectionPointer->setDefaultShortcut(newWindowActionPointer, ctrlNKeySequence);
+    actionCollectionPointer->setDefaultShortcut(saveArchiveActionPointer, ctrlAKeySequence);
     actionCollectionPointer->setDefaultShortcut(zoomDefaultActionPointer, ctrl0KeySequence);
     actionCollectionPointer->setDefaultShortcut(reloadAndBypassCacheActionPointer, ctrlF5KeySequence);
     actionCollectionPointer->setDefaultShortcut(viewSourceActionPointer, ctrlUKeySequence);
@@ -306,6 +311,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     // Execute the actions.
     connect(newTabActionPointer, SIGNAL(triggered()), tabWidgetPointer, SLOT(addTab()));
     connect(newWindowActionPointer, SIGNAL(triggered()), this, SLOT(newWindow()));
+    connect(saveArchiveActionPointer, SIGNAL(triggered()), tabWidgetPointer, SLOT(saveArchive()));
     connect(zoomDefaultActionPointer, SIGNAL(triggered()), this, SLOT(zoomDefault()));
     connect(reloadAndBypassCacheActionPointer, SIGNAL(triggered()), this, SLOT(reloadAndBypassCache()));
     connect(viewSourceActionPointer, SIGNAL(triggered()), this, SLOT(toggleViewSource()));