diff --git a/dist/changes-5.9.4 b/dist/changes-5.9.4
new file mode 100644
index 0000000000000000000000000000000000000000..9a713660a3d44e969470579258fc7f0dcb095c40
--- /dev/null
+++ b/dist/changes-5.9.4
@@ -0,0 +1,28 @@
+Qt 5.9.4 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.9.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.9 series is binary compatible with the 5.8.x series.
+Applications compiled for 5.8 will continue to run with 5.9.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                               Qt 5.9.4 Changes                           *
+****************************************************************************
+
+Build system
+------------
+
+ - [QTBUG-64317] Fixed qt5_add_translation() CMake macro for TS files whose
+   names contain multiple dots.
diff --git a/examples/assistant/simpletextviewer/assistant.cpp b/examples/assistant/simpletextviewer/assistant.cpp
index 9e668bb7fc191b2f48f13ab3fef678b94c0e6262..58103c14a358852bb98e77eff605eaa587463244 100644
--- a/examples/assistant/simpletextviewer/assistant.cpp
+++ b/examples/assistant/simpletextviewer/assistant.cpp
@@ -108,8 +108,9 @@ bool Assistant::startAssistant()
         proc->start(app, args);
 
         if (!proc->waitForStarted()) {
-            QMessageBox::critical(nullptr, QObject::tr("Simple Text Viewer"),
-                QObject::tr("Unable to launch Qt Assistant (%1)").arg(app));
+            QMessageBox::critical(nullptr,
+                                  tr("Simple Text Viewer"),
+                                  tr("Unable to launch Qt Assistant (%1)").arg(app));
             return false;
         }
     }
diff --git a/examples/assistant/simpletextviewer/assistant.h b/examples/assistant/simpletextviewer/assistant.h
index dada5a1ee7015a8b060cc9dfaf7267b8020464ec..1ea7dd78f2ffca4855de1fb9115ddcbeaa5acc2c 100644
--- a/examples/assistant/simpletextviewer/assistant.h
+++ b/examples/assistant/simpletextviewer/assistant.h
@@ -51,6 +51,7 @@
 #ifndef ASSISTANT_H
 #define ASSISTANT_H
 
+#include <QCoreApplication>
 #include <QString>
 
 QT_BEGIN_NAMESPACE
@@ -59,6 +60,8 @@ QT_END_NAMESPACE
 
 class Assistant
 {
+    Q_DECLARE_TR_FUNCTIONS(Assistant)
+
 public:
     Assistant();
     ~Assistant();
diff --git a/src/assistant/assistant/bookmarkmanager.cpp b/src/assistant/assistant/bookmarkmanager.cpp
index 9016c2c6e45eaf2582d5ac3ad70ae384ba8bac9d..fe1b0406764e91afaa34833454fe668748ae3d04 100644
--- a/src/assistant/assistant/bookmarkmanager.cpp
+++ b/src/assistant/assistant/bookmarkmanager.cpp
@@ -81,6 +81,12 @@ void BookmarkManager::BookmarkTreeView::subclassKeyPressEvent(QKeyEvent *event)
     QTreeView::keyPressEvent(event);
 }
 
+void BookmarkManager::BookmarkTreeView::commitData(QWidget *editor)
+{
+    QTreeView::commitData(editor);
+    emit editingDone();
+}
+
 void BookmarkManager::BookmarkTreeView::setExpandedData(const QModelIndex &index)
 {
     TRACE_OBJ
@@ -142,6 +148,8 @@ void BookmarkManager::addBookmark(const QString &title, const QString &url)
     TRACE_OBJ
     showBookmarkDialog(title.isEmpty() ? tr("Untitled") : title,
         url.isEmpty() ? QLatin1String("about:blank") : url);
+
+    storeBookmarks();
 }
 
 // -- private
@@ -178,6 +186,8 @@ BookmarkManager::BookmarkManager()
             [this](const QModelIndex &index) { setSourceFromIndex(index, false); });
     connect(bookmarkTreeView, &QWidget::customContextMenuRequested,
             this, &BookmarkManager::customContextMenuRequested);
+    connect(bookmarkTreeView, &BookmarkTreeView::editingDone,
+            this, &BookmarkManager::storeBookmarks);
 
     connect(&HelpEngineWrapper::instance(), &HelpEngineWrapper::setupFinished,
             this, &BookmarkManager::setupFinished);
@@ -195,13 +205,14 @@ BookmarkManager::BookmarkManager()
             this, &BookmarkManager::refreshBookmarkToolBar);
     connect(bookmarkModel, &QAbstractItemModel::dataChanged,
             this, &BookmarkManager::refreshBookmarkToolBar);
+
 }
 
 BookmarkManager::~BookmarkManager()
 {
     TRACE_OBJ
     delete bookmarkManagerWidget;
-    HelpEngineWrapper::instance().setBookmarks(bookmarkModel->bookmarks());
+    storeBookmarks();
     delete bookmarkModel;
 }
 
@@ -225,6 +236,8 @@ void BookmarkManager::removeItem(const QModelIndex &index)
             return;
     }
     bookmarkModel->removeItem(current);
+
+    storeBookmarks();
 }
 
 bool BookmarkManager::eventFilter(QObject *object, QEvent *event)
@@ -332,6 +345,11 @@ void BookmarkManager::setupFinished()
     typeAndSearchModel->setSourceModel(bookmarkFilterModel);
 }
 
+void BookmarkManager::storeBookmarks()
+{
+    HelpEngineWrapper::instance().setBookmarks(bookmarkModel->bookmarks());
+}
+
 void BookmarkManager::addBookmarkActivated()
 {
     TRACE_OBJ
@@ -517,6 +535,8 @@ void BookmarkManager::managerWidgetAboutToClose()
 {
     delete bookmarkManagerWidget;
     bookmarkManagerWidget = 0;
+
+    storeBookmarks();
 }
 
 void BookmarkManager::textChanged(const QString &text)
diff --git a/src/assistant/assistant/bookmarkmanager.h b/src/assistant/assistant/bookmarkmanager.h
index fcdebf4d1b05f504fc4027585493e64e86f852e6..ac3a13a9eaaa78cf1f2ee42fb3c85626e8f50029 100644
--- a/src/assistant/assistant/bookmarkmanager.h
+++ b/src/assistant/assistant/bookmarkmanager.h
@@ -77,6 +77,7 @@ private:
 
 private slots:
     void setupFinished();
+    void storeBookmarks();
 
     void addBookmarkActivated();
     void removeBookmarkActivated();
@@ -137,6 +138,12 @@ public:
 
     void subclassKeyPressEvent(QKeyEvent *event);
 
+signals:
+    void editingDone();
+
+protected slots:
+    void commitData(QWidget *editor);
+
 private slots:
     void setExpandedData(const QModelIndex &index);
 };
diff --git a/src/assistant/assistant/helpviewer.cpp b/src/assistant/assistant/helpviewer.cpp
index 2ea72270b5519cd41d8c1846b4a9d1c338ad233f..8c97f463f86be212920431253df5110654996c14 100644
--- a/src/assistant/assistant/helpviewer.cpp
+++ b/src/assistant/assistant/helpviewer.cpp
@@ -72,7 +72,7 @@ struct ExtensionMap {
     { ".mng", "video/x-mng" },
     { ".pbm", "image/x-portable-bitmap" },
     { ".pgm", "image/x-portable-graymap" },
-    { ".pdf", "application/pdf" },
+    { ".pdf", 0 },
     { ".png", "image/png" },
     { ".ppm", "image/x-portable-pixmap" },
     { ".rss", "application/rss+xml" },
@@ -156,7 +156,7 @@ bool HelpViewer::launchWithExternalApp(const QUrl &url)
 
             actualTmpFile.write(helpEngine.fileData(resolvedUrl));
             actualTmpFile.close();
-            return QDesktopServices::openUrl(QUrl(actualTmpFile.fileName()));
+            return QDesktopServices::openUrl(QUrl::fromLocalFile(actualTmpFile.fileName()));
         }
         return false;
     }
diff --git a/src/assistant/help/qhelpsearchindexwriter_default.cpp b/src/assistant/help/qhelpsearchindexwriter_default.cpp
index 2dbbb30d2dc206a988cef529e453039fd3732d5a..72e92ecab9814360c913df408784ebd7409658eb 100644
--- a/src/assistant/help/qhelpsearchindexwriter_default.cpp
+++ b/src/assistant/help/qhelpsearchindexwriter_default.cpp
@@ -501,11 +501,18 @@ void QHelpSearchIndexWriter::run()
                 if (text.isEmpty())
                     continue;
 
-                QTextDocument doc;
-                doc.setHtml(text);
-
-                const QString &title = doc.metaInformation(QTextDocument::DocumentTitle).toHtmlEscaped();
-                const QString &contents = doc.toPlainText().toHtmlEscaped();
+                QString title;
+                QString contents;
+                if (url.endsWith(QLatin1String(".txt"))) {
+                    title = url.mid(url.lastIndexOf(QLatin1Char('/')) + 1);
+                    contents = text.toHtmlEscaped();
+                } else {
+                    QTextDocument doc;
+                    doc.setHtml(text);
+
+                    title = doc.metaInformation(QTextDocument::DocumentTitle).toHtmlEscaped();
+                    contents = doc.toPlainText().toHtmlEscaped();
+                }
 
                 writer.insertDoc(namespaceName, attributesString, url, title, contents);
             }
diff --git a/src/linguist/Qt5LinguistToolsMacros.cmake b/src/linguist/Qt5LinguistToolsMacros.cmake
index cb158cbc6f7b97add377d87eea03ff78b6c50ebf..b9959730473819c9ade54e73fb9ca143e9e96951 100644
--- a/src/linguist/Qt5LinguistToolsMacros.cmake
+++ b/src/linguist/Qt5LinguistToolsMacros.cmake
@@ -91,13 +91,15 @@ function(QT5_ADD_TRANSLATION _qm_files)
 
     foreach(_current_FILE ${_lrelease_files})
         get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE)
-        get_filename_component(qm ${_abs_FILE} NAME_WE)
+        get_filename_component(qm ${_abs_FILE} NAME)
+        # everything before the last dot has to be considered the file name (including other dots)
+        string(REGEX REPLACE "\\.[^.]*$" "" FILE_NAME ${qm})
         get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION)
         if(output_location)
             file(MAKE_DIRECTORY "${output_location}")
-            set(qm "${output_location}/${qm}.qm")
+            set(qm "${output_location}/${FILE_NAME}.qm")
         else()
-            set(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm")
+            set(qm "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.qm")
         endif()
 
         add_custom_command(OUTPUT ${qm}
diff --git a/src/windeployqt/main.cpp b/src/windeployqt/main.cpp
index 1be7338e4cf19829a9a18b39d87102a84b84fb94..0681a5aa84a98aa7a2dae04567cba1d96b3b4608 100644
--- a/src/windeployqt/main.cpp
+++ b/src/windeployqt/main.cpp
@@ -100,7 +100,8 @@ enum QtModule
     QtWebChannelModule        = 0x0000200000000000,
     QtTextToSpeechModule      = 0x0000400000000000,
     QtSerialBusModule         = 0x0000800000000000,
-    QtGamePadModule           = 0x0001000000000000
+    QtGamePadModule           = 0x0001000000000000,
+    Qt3DAnimationModule       = 0x0002000000000000
 };
 
 struct QtModuleEntry {
@@ -156,6 +157,7 @@ static QtModuleEntry qtModuleEntries[] = {
     { Qt3DQuickModule, "3dquick", "Qt53DQuick", 0 },
     { Qt3DQuickRendererModule, "3dquickrenderer", "Qt53DQuickRender", 0 },
     { Qt3DInputModule, "3dinput", "Qt53DInput", 0 },
+    { Qt3DAnimationModule, "3danimation", "Qt53DAnimation", 0 },
     { QtLocationModule, "geoservices", "Qt5Location", 0 },
     { QtWebChannelModule, "webchannel", "Qt5WebChannel", 0 },
     { QtTextToSpeechModule, "texttospeech", "Qt5TextToSpeech", 0 },
@@ -1119,9 +1121,12 @@ static QStringList compilerRunTimeLibs(Platform platform, bool isDebug, unsigned
             const QStringList countryCodes = vcRedistDir.entryList(QStringList(QStringLiteral("[0-9]*")), QDir::Dirs);
             if (!countryCodes.isEmpty()) // Pre MSVC2017
                 releaseRedistDir += QLatin1Char('/') + countryCodes.constFirst();
-            const QFileInfo fi(releaseRedistDir + QLatin1Char('/')
-                               + QStringLiteral("vcredist_") + wordSizeString
-                               + QStringLiteral(".exe"));
+            QFileInfo fi(releaseRedistDir + QLatin1Char('/') + QStringLiteral("vc_redist.")
+                         + wordSizeString + QStringLiteral(".exe"));
+            if (!fi.isFile()) { // Pre MSVC2017/15.5
+                fi.setFile(releaseRedistDir + QLatin1Char('/') + QStringLiteral("vcredist_")
+                           + wordSizeString + QStringLiteral(".exe"));
+            }
             if (fi.isFile())
                 redistFiles.append(fi.absoluteFilePath());
         }
diff --git a/src/winrtrunner/appxlocalengine.cpp b/src/winrtrunner/appxlocalengine.cpp
index eb21d81a74e8582fc88458d2b18f0cc7c47c477f..4d72633e181b2faa6750e7003d6247721f28f960 100644
--- a/src/winrtrunner/appxlocalengine.cpp
+++ b/src/winrtrunner/appxlocalengine.cpp
@@ -349,8 +349,6 @@ AppxLocalEngine::AppxLocalEngine(Runner *runner)
 
 AppxLocalEngine::~AppxLocalEngine()
 {
-    Q_D(const AppxLocalEngine);
-    CloseHandle(d->processHandle);
 }
 
 bool AppxLocalEngine::installPackage(IAppxManifestReader *reader, const QString &filePath)