diff --git a/src/core/Info_mac.plist b/src/core/Info_mac.plist
new file mode 100644
index 0000000000000000000000000000000000000000..58362c810013fb9350d7b69d895d792f9c5eed0f
--- /dev/null
+++ b/src/core/Info_mac.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>@SHORT_VERSION@</string>
+	<key>CFBundleVersion</key>
+	<string>@FULL_VERSION@</string>
+	<key>CFBundleGetInfoString</key>
+	<string>Created by Qt/QMake</string>
+	<key>CFBundleSignature</key>
+	<string>@TYPEINFO@</string>
+	<key>CFBundleExecutable</key>
+	<string>@LIBRARY@</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.qt-project.Qt.QtWebEngineCore</string>
+</dict>
+</plist>
diff --git a/src/core/core_module.pro b/src/core/core_module.pro
index 3ba65e3017603bd63199baae18d3dcd5c8b8d08f..66ab3ee4d81c8c6469e1c0571cd9997c4919e127 100644
--- a/src/core/core_module.pro
+++ b/src/core/core_module.pro
@@ -6,18 +6,21 @@ CMAKE_MODULE_TESTS = "-"
 QT += qml quick
 QT_PRIVATE += gui-private
 
+# Needed to set a CFBundleIdentifier
+QMAKE_INFO_PLIST = Info_mac.plist
+
 # Look for linking information produced by gyp for our target according to core_generated.gyp
 !include($$OUT_PWD/$$getConfigDir()/$${TARGET}_linking.pri) {
     error("Could not find the linking information that gyp should have generated.")
 }
 
 REPACK_DIR = $$OUT_PWD/$$getConfigDir()/gen/repack
-locales.files = "$$REPACK_DIR/qtwebengine_locales/*"
-locales.CONFIG += no_check_exist
-locales.path = $$[QT_INSTALL_TRANSLATIONS]/qtwebengine_locales
+# Duplicated from resources/resources.gyp
+LOCALE_LIST = am ar bg bn ca cs da de el en-GB en-US es-419 es et fa fi fil fr gu he hi hr hu id it ja kn ko lt lv ml mr ms nb nl pl pt-BR pt-PT ro ru sk sl sr sv sw ta te th tr uk vi zh-CN zh-TW
+for(LOC, LOCALE_LIST) {
+    locales.files += $$REPACK_DIR/qtwebengine_locales/$${LOC}.pak
+}
 resources.files = $$REPACK_DIR/qtwebengine_resources.pak
-resources.CONFIG += no_check_exist
-resources.path = $$[QT_INSTALL_DATA]
 
 PLUGIN_EXTENSION = .so
 PLUGIN_PREFIX = lib
@@ -26,16 +29,34 @@ win32 {
     PLUGIN_EXTENSION = .dll
     PLUGIN_PREFIX =
 }
-
 icu.files = $$OUT_PWD/$$getConfigDir()/icudtl.dat
-icu.CONFIG += no_check_exist
-icu.path = $$[QT_INSTALL_DATA]
 
 plugins.files = $$OUT_PWD/$$getConfigDir()/$${PLUGIN_PREFIX}ffmpegsumo$${PLUGIN_EXTENSION}
-plugins.CONFIG += no_check_exist
-plugins.path = $$[QT_INSTALL_PLUGINS]/qtwebengine
 
-INSTALLS += icu locales resources plugins
+!debug_and_release|!build_all|CONFIG(release, debug|release):contains(QT_CONFIG, qt_framework) {
+    locales.version = Versions
+    locales.path = Resources/qtwebengine_locales
+    resources.version = Versions
+    resources.path = Resources
+    icu.version = Versions
+    icu.path = Resources
+    plugins.version = Versions
+    plugins.path = Libraries
+    # No files, this prepares the bundle Helpers symlink, process.pro will create the directories
+    qtwebengineprocessplaceholder.version = Versions
+    qtwebengineprocessplaceholder.path = Helpers
+    QMAKE_BUNDLE_DATA += icu locales resources plugins qtwebengineprocessplaceholder
+} else {
+    locales.CONFIG += no_check_exist
+    locales.path = $$[QT_INSTALL_TRANSLATIONS]/qtwebengine_locales
+    resources.CONFIG += no_check_exist
+    resources.path = $$[QT_INSTALL_DATA]
+    icu.CONFIG += no_check_exist
+    icu.path = $$[QT_INSTALL_DATA]
+    plugins.CONFIG += no_check_exist
+    plugins.path = $$[QT_INSTALL_PLUGINS]/qtwebengine
+    INSTALLS += icu locales resources plugins
+}
 
 # We distribute the module binary but headers are only available in-tree.
 CONFIG += no_module_headers
diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp
index 4cdf3813e89c6d105083f4cc35f62353523af1a3..026f4db83f62017b7fb53873fd86964f03b37d29 100644
--- a/src/core/web_engine_library_info.cpp
+++ b/src/core/web_engine_library_info.cpp
@@ -92,6 +92,39 @@ QString location(QLibraryInfo::LibraryLocation path)
     return QLibraryInfo::location(path);
 }
 
+#if defined(OS_MACOSX)
+static inline CFBundleRef frameworkBundle()
+{
+    return CFBundleGetBundleWithIdentifier(CFSTR("org.qt-project.Qt.QtWebEngineCore"));
+}
+
+static QString getPath(CFBundleRef frameworkBundle)
+{
+    QString path;
+    if (frameworkBundle) {
+        CFURLRef bundleUrl = CFBundleCopyBundleURL(frameworkBundle);
+        CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleUrl, kCFURLPOSIXPathStyle);
+        path = QString::fromCFString(bundlePath);
+        CFRelease(bundlePath);
+        CFRelease(bundleUrl);
+    }
+    return path;
+}
+
+static QString getResourcesPath(CFBundleRef frameworkBundle)
+{
+    QString path;
+    if (frameworkBundle) {
+        CFURLRef resourcesRelativeUrl = CFBundleCopyResourcesDirectoryURL(frameworkBundle);
+        CFStringRef resourcesRelativePath = CFURLCopyFileSystemPath(resourcesRelativeUrl, kCFURLPOSIXPathStyle);
+        path = getPath(frameworkBundle) % QLatin1Char('/') % QString::fromCFString(resourcesRelativePath);
+        CFRelease(resourcesRelativePath);
+        CFRelease(resourcesRelativeUrl);
+    }
+    return path;
+}
+#endif
+
 QString subProcessPath()
 {
     static bool initialized = false;
@@ -100,14 +133,19 @@ QString subProcessPath()
 #else
     static QString processBinary (QLatin1String(QTWEBENGINEPROCESS_NAME));
 #endif
+#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD)
+    static QString processPath (getPath(frameworkBundle())
+                                % QStringLiteral("/Helpers/" QTWEBENGINEPROCESS_NAME ".app/Contents/MacOS/" QTWEBENGINEPROCESS_NAME));
+#else
     static QString processPath (location(QLibraryInfo::LibraryExecutablesPath)
                                 % QDir::separator() % processBinary);
+#endif
     if (!initialized) {
         // Allow overriding at runtime for the time being.
         const QByteArray fromEnv = qgetenv("QTWEBENGINEPROCESS_PATH");
         if (!fromEnv.isEmpty())
             processPath = QString::fromLatin1(fromEnv);
-        if (processPath.isEmpty() || !QFileInfo(processPath).exists()) {
+        if (!QFileInfo(processPath).exists()) {
             qWarning("QtWebEngineProcess not found at location %s. Trying fallback path...", qPrintable(processPath));
             processPath = QCoreApplication::applicationDirPath() % QDir::separator() % processBinary;
         }
@@ -121,12 +159,20 @@ QString subProcessPath()
 
 QString pluginsPath()
 {
+#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD)
+    return getPath(frameworkBundle()) % QLatin1String("/Libraries");
+#else
     return location(QLibraryInfo::PluginsPath) % QDir::separator() % QLatin1String("qtwebengine");
+#endif
 }
 
 QString localesPath()
 {
+#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD)
+    return getResourcesPath(frameworkBundle()) % QLatin1String("/qtwebengine_locales");
+#else
     return location(QLibraryInfo::TranslationsPath) % QLatin1String("/qtwebengine_locales");
+#endif
 }
 
 QString fallbackDir() {
@@ -155,7 +201,11 @@ base::FilePath WebEngineLibraryInfo::getPath(int key)
     QString directory;
     switch (key) {
     case QT_RESOURCES_PAK:
+#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD)
+        return toFilePath(getResourcesPath(frameworkBundle()) % QLatin1String("/qtwebengine_resources.pak"));
+#else
         return toFilePath(location(QLibraryInfo::DataPath) % QLatin1String("/qtwebengine_resources.pak"));
+#endif
     case base::FILE_EXE:
     case content::CHILD_PROCESS_EXE:
         return toFilePath(subProcessPath());
@@ -171,7 +221,11 @@ base::FilePath WebEngineLibraryInfo::getPath(int key)
         directory = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
         break;
     case base::DIR_QT_LIBRARY_DATA:
+#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD)
+        return toFilePath(getResourcesPath(frameworkBundle()));
+#else
         return toFilePath(location(QLibraryInfo::DataPath));
+#endif
 #if defined(OS_ANDROID)
     case base::DIR_SOURCE_ROOT:
     case base::DIR_ANDROID_EXTERNAL_STORAGE:
diff --git a/src/process/process.pro b/src/process/process.pro
index a9f5d183f1c99ad71be337232deba247e5e18d2a..8ed09612a4c8ab129878e5640377b835baf7c3a6 100644
--- a/src/process/process.pro
+++ b/src/process/process.pro
@@ -3,14 +3,22 @@ TEMPLATE = app
 
 QT_PRIVATE += webenginecore
 
-CONFIG -= app_bundle
-
 load(qt_build_paths)
-DESTDIR = $$MODULE_BASE_OUTDIR/libexec
+contains(QT_CONFIG, qt_framework) {
+    # Deploy the QtWebEngineProcess app bundle into the QtWebEngineCore framework.
+    DESTDIR = $$MODULE_BASE_OUTDIR/lib/QtWebEngineCore.framework/Versions/5/Helpers
+} else {
+    CONFIG -= app_bundle
+    DESTDIR = $$MODULE_BASE_OUTDIR/libexec
+}
 
 INCLUDEPATH += ../core
 
 SOURCES = main.cpp
 
-target.path = $$[QT_INSTALL_LIBEXECS]
+contains(QT_CONFIG, qt_framework) {
+    target.path = $$[QT_INSTALL_LIBS]/QtWebEngineCore.framework/Versions/5/Helpers
+} else {
+    target.path = $$[QT_INSTALL_LIBEXECS]
+}
 INSTALLS += target