diff --git a/examples/examples.pro b/examples/examples.pro index f15007e8375f9a9d93c17cdba7c9875993718dd0..0e222de73faed3822d763b33b733cf8159e27338 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -15,8 +15,12 @@ qtHaveModule(webenginewidgets) { webenginewidgets/markdowneditor \ webenginewidgets/simplebrowser - !contains(WEBENGINE_CONFIG, no_spellcheck):!osx:!cross_compile { - SUBDIRS += webenginewidgets/spellchecker + !contains(WEBENGINE_CONFIG, no_spellcheck):!cross_compile { + !contains(WEBENGINE_CONFIG, use_native_spellchecker) { + SUBDIRS += webenginewidgets/spellchecker + } else { + message("Spellcheck example will not be built because it depends on usage of Hunspell dictionaries.") + } } } diff --git a/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc b/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc index d5b972b93f2532aade5259f116797c9b18c8d886..b0240cd4d2f424224199b7e49c61fea9de199462 100644 --- a/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc +++ b/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc @@ -41,12 +41,12 @@ \section1 Dictionaries To be able to check the spelling, we need to provide the spellchecker with - dictionaries. In this example, we want to support the English and German - languages. + dictionaries. The Qt WebEngine spellchecker supports dictionaries provided by the + \l {Hunspell project} on all platforms and native dictionaries provided by macOS. + In this example, we want to support the English and German languages. - The Qt WebEngine spellchecker supports dictionaries from the - \l{Hunspell project}, but they have to be compiled into a special binary - format. A Hunspell dictionary consists of two files: + For Hunspell dictionaries to be supported they have to be compiled into a special binary format. + A Hunspell dictionary consists of two files: \list @@ -78,7 +78,12 @@ When a specific spellchecking language is requested, Qt WebEngine will try to load the already compiled matching \c .bdic file first from \e qtwebengine_dictionaries directories relative to the executable, - then it will look in \c QT_INSTALL_PREFIX/qtwebengines_dictionaries. + then it will look in \c QT_INSTALL_PREFIX/qtwebengine_dictionaries. + + On macOS, because this example was configured to use Hunspell dictionaries, Qt WebEngine will + look in the \e qtwebengine_dictionaries directory located inside the application bundle + \c Resources directory, and also in the \c Resources directory located inside the + Qt framework bundle. We specify the QMAKE_EXTRA_COMPILERS parameter in the project file to add a conversion step to the build process: @@ -87,11 +92,11 @@ \skipto CONVERT_TOOL \printuntil QMAKE_EXTRA_COMPILERS - To set up a dictionary, we run \c qwebengine_convert_dic passing the + To set up a dictionary, we run \c qwebengine_convert_dict passing the file path of the dictionary \c dic and \c bdic files. The \c aff file and optional \c delta file are also picked up by the \c convert process. The output \c bdic file is placed into the \e qtwebengine_dictionaries local - directory, which the application binary will run from. + directory (or Resources directory), which the application binary will run from. \section1 Setting the Spellchecker diff --git a/examples/webenginewidgets/spellchecker/spellchecker.pro b/examples/webenginewidgets/spellchecker/spellchecker.pro index c7bee6584fa774aedfae2cb44458d96f46a8c7e3..c6031a36d727d102bd047696ebbc643fb07b54ef 100644 --- a/examples/webenginewidgets/spellchecker/spellchecker.pro +++ b/examples/webenginewidgets/spellchecker/spellchecker.pro @@ -3,6 +3,10 @@ TARGET = spellchecker QT += webenginewidgets CONFIG += c++11 +contains(WEBENGINE_CONFIG, use_native_spellchecker) { + error("Spellcheck example can not be built when using native OS dictionaries.") +} + HEADERS += \ webview.h @@ -33,7 +37,11 @@ debug_and_release { DICTIONARIES_DIR = qtwebengine_dictionaries } -dict.files = $$PWD/dict/en/en-US.dic $$PWD/dict/de/de-DE.dic +dict_base_paths = en/en-US de/de-DE +for (base_path, dict_base_paths) { + dict.files += $$PWD/dict/$${base_path}.dic +} + dictoolbuild.input = dict.files dictoolbuild.output = $${DICTIONARIES_DIR}/${QMAKE_FILE_BASE}.bdic dictoolbuild.depends = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.aff @@ -41,3 +49,15 @@ dictoolbuild.commands = $${CONVERT_TOOL} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} dictoolbuild.name = Build ${QMAKE_FILE_IN_BASE} dictoolbuild.CONFIG = no_link target_predeps QMAKE_EXTRA_COMPILERS += dictoolbuild + +# When the example is compiled as a bundle, WebEngine expects to find the dictionaries in +# bundle.app/Contents/Resources/qtwebengine_dictionaries +macos:app_bundle { + for (base_path, dict_base_paths) { + base_path_splitted = $$split(base_path, /) + base_name = $$last(base_path_splitted) + binary_dict_files.files += $${DICTIONARIES_DIR}/$${base_name}.bdic + } + binary_dict_files.path = Contents/Resources/$$DICTIONARIES_DIR + QMAKE_BUNDLE_DATA += binary_dict_files +} diff --git a/src/core/chrome_qt.gyp b/src/core/chrome_qt.gyp index 3fa2625a8a6a6597982ee81a791fab21bbbbc85c..89f1fe1886781677e4b552922dd703ee75088147 100644 --- a/src/core/chrome_qt.gyp +++ b/src/core/chrome_qt.gyp @@ -132,6 +132,8 @@ 'sources!': [ '<(DEPTH)/chrome/renderer/spellchecker/platform_spelling_engine.cc', '<(DEPTH)/chrome/renderer/spellchecker/platform_spelling_engine.h', + '<(DEPTH)/chrome/browser/spellchecker/spellcheck_message_filter_platform.h', + '<(DEPTH)/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac.cc', ], }], ], diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 64964dc09124e89c56c2ca63a735f5fc6b17945c..a57c3b00fb8fb64876eb86c6892cac1cdeba8295 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -44,6 +44,9 @@ #include "base/threading/thread_restrictions.h" #if defined(ENABLE_SPELLCHECK) #include "chrome/browser/spellchecker/spellcheck_message_filter.h" +#if defined(USE_BROWSER_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_message_filter_platform.h" +#endif #endif #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/public/browser/browser_main_parts.h" @@ -377,8 +380,12 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost* host->AddFilter(new BrowserMessageFilterQt(id)); #endif #if defined(ENABLE_SPELLCHECK) + // SpellCheckMessageFilter is required for both Hunspell and Native configurations. host->AddFilter(new SpellCheckMessageFilter(id)); #endif +#if defined(Q_OS_MACOS) && defined(USE_BROWSER_SPELLCHECKER) + host->AddFilter(new SpellCheckMessageFilterPlatform(id)); +#endif #if defined(ENABLE_BASIC_PRINTING) host->AddFilter(new PrintingMessageFilterQt(host->GetID())); #endif // defined(ENABLE_BASIC_PRINTING) diff --git a/src/core/gyp_run.pro b/src/core/gyp_run.pro index 89b751c4ab492205f076b03b99426c5a631217d3..1c62c0cfd2527c743b5dbfe0ccbfb5f56f2c7ce8 100644 --- a/src/core/gyp_run.pro +++ b/src/core/gyp_run.pro @@ -124,10 +124,16 @@ contains(WEBENGINE_CONFIG, reduce_binary_size): GYP_CONFIG += release_optimize=s contains(WEBENGINE_CONFIG, no_spellcheck): { GYP_CONFIG += enable_spellcheck=0 - osx: GYP_CONFIG += use_browser_spellchecker=0 + macos: GYP_CONFIG += use_browser_spellchecker=0 } else { GYP_CONFIG += enable_spellcheck=1 - osx: GYP_CONFIG += use_browser_spellchecker=1 + macos { + contains(WEBENGINE_CONFIG, use_native_spellchecker) { + GYP_CONFIG += use_browser_spellchecker=1 + } else { + GYP_CONFIG += use_browser_spellchecker=0 + } + } } !qtConfig(framework):qtConfig(private_tests) { diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp index 2be59d1c67531c8fde96887600cf83f4b174fbd2..40977812d9f7ae22e1a6865faa8a66e6dd4b7055 100644 --- a/src/core/web_engine_library_info.cpp +++ b/src/core/web_engine_library_info.cpp @@ -69,7 +69,7 @@ QString fallbackDir() { return directory; } -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD) static inline CFBundleRef frameworkBundle() { return CFBundleGetBundleWithIdentifier(CFSTR("org.qt-project.Qt.QtWebEngineCore")); @@ -112,6 +112,34 @@ static QString getResourcesPath(CFBundleRef frameworkBundle) } #endif +#if defined(OS_MACOSX) +static QString getMainApplicationResourcesPath() +{ + QString resourcesPath; + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if (!mainBundle) + return resourcesPath; + + // Will point to Resources inside an app bundle, or in case if the application is not packaged + // as a bundle, will point to the application directory, where the resources are assumed to be + // found. + CFURLRef resourcesRelativeUrl = CFBundleCopyResourcesDirectoryURL(mainBundle); + if (!resourcesRelativeUrl) + return resourcesPath; + + CFURLRef resourcesAbsoluteUrl = CFURLCopyAbsoluteURL(resourcesRelativeUrl); + CFStringRef resourcesAbolutePath = CFURLCopyFileSystemPath(resourcesAbsoluteUrl, + kCFURLPOSIXPathStyle); + resourcesPath = QString::fromCFString(resourcesAbolutePath); + CFRelease(resourcesAbolutePath); + CFRelease(resourcesAbsoluteUrl); + CFRelease(resourcesRelativeUrl); + + return resourcesPath; +} + +#endif + QString subProcessPath() { static QString processPath; @@ -181,18 +209,42 @@ QString localesPath() #if defined(ENABLE_SPELLCHECK) QString dictionariesPath() { + static QString potentialDictionariesPath; + static bool initialized = false; + QStringList candidatePaths; + if (!initialized) { + initialized = true; + + // First try to find dictionaries near the application. +#ifdef OS_MACOSX + QString resourcesDictionariesPath = getMainApplicationResourcesPath() + % QDir::separator() % QLatin1String("qtwebengine_dictionaries"); + candidatePaths << resourcesDictionariesPath; +#endif + QString applicationDictionariesPath = QCoreApplication::applicationDirPath() + % QDir::separator() % QLatin1String("qtwebengine_dictionaries"); + candidatePaths << applicationDictionariesPath; + + // Then try to find dictionaries near the installed library. #if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD) - return getResourcesPath(frameworkBundle()) % QLatin1String("/qtwebengine_dictionaries"); -#else - // first local path - static QString potentialDictionariesPath = QCoreApplication::applicationDirPath() % QDir::separator() % QLatin1String("qtwebengine_dictionaries"); + QString frameworkDictionariesPath = getResourcesPath(frameworkBundle()) + % QLatin1String("/qtwebengine_dictionaries"); + candidatePaths << frameworkDictionariesPath; +#endif + + QString libraryDictionariesPath = QLibraryInfo::location(QLibraryInfo::DataPath) + % QDir::separator() % QLatin1String("qtwebengine_dictionaries"); + candidatePaths << libraryDictionariesPath; - // now global one - if (!QFileInfo::exists(potentialDictionariesPath)) - potentialDictionariesPath = QLibraryInfo::location(QLibraryInfo::DataPath) % QDir::separator() % QLatin1String("qtwebengine_dictionaries"); + Q_FOREACH (const QString &candidate, candidatePaths) { + if (QFileInfo::exists(candidate)) { + potentialDictionariesPath = candidate; + break; + } + } + } return potentialDictionariesPath; -#endif } #endif // ENABLE_SPELLCHECK diff --git a/src/src.pro b/src/src.pro index dc6bc5274f362be4456d64b7998a7ad7f6bb48e5..495263846b9091a3aaf17bdf9cb09af8773813ec 100644 --- a/src/src.pro +++ b/src/src.pro @@ -14,8 +14,7 @@ SUBDIRS += core \ plugins # allow only desktop builds of qwebengine_convert_dict -# osx does not use hunspell -!contains(WEBENGINE_CONFIG, no_spellcheck):!osx:!cross_compile { +!contains(WEBENGINE_CONFIG, no_spellcheck):!contains(WEBENGINE_CONFIG, use_native_spellchecker):!cross_compile { SUBDIRS += qwebengine_convert_dict qwebengine_convert_dict.subdir = tools/qwebengine_convert_dict qwebengine_convert_dict.depends = core diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 1ca97adeaaaadfe2d48b5f5e1420accee84b5321..6a4846ddf454952df89024cebc4ad314cf857f55 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -632,13 +632,33 @@ QQuickWebEngineProfile *QQuickWebEngineProfile::defaultProfile() For example, the \a language \c en-US will load the \c en-US.bdic dictionary file. - The web engine checks for the \c qtwebengine_dictionaries subdirectory - first in the local directory and if it is not found in the Qt - installation directory: + Qt WebEngine checks for the \c qtwebengine_dictionaries subdirectory + first in the local directory and if it is not found, in the Qt + installation directory. + + On macOS, depending on how Qt WebEngine is configured at build time, there are two possibilities + how spellchecking data is found: + + \list + \li Hunspell dictionaries (default) - .bdic dictionaries are used, just like on other + platforms + \li Native dictionaries - the macOS spellchecking APIs are used (which means the results + will depend on the installed OS dictionaries) + \endlist + + Thus, in the macOS Hunspell case, Qt WebEngine will look in the \e qtwebengine_dictionaries + subdirectory located inside the application bundle \c Resources directory, and also in the + \c Resources directory located inside the Qt framework bundle. + + To summarize, in case of Hunspell usage, the following paths are considered: \list \li QCoreApplication::applicationDirPath()/qtwebengine_dictionaries + or QCoreApplication::applicationDirPath()/../Contents/Resources/qtwebengine_dictionaries + (on macOS) \li [QLibraryInfo::DataPath]/qtwebengine_dictionaries + or path/to/QtWebEngineCore.framework/Resources/qtwebengine_dictionaries (Qt framework + bundle on macOS) \endlist For more information about how to compile \c .bdic dictionaries, see the diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index a400b579228b9fd46e11f7201050ce45e781dab9..bd41a5126c665b5ede5939912a78214a6f0aecfa 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -570,13 +570,33 @@ QWebEngineProfile *QWebEngineProfile::defaultProfile() For example, the \a language \c en-US will load the \c en-US.bdic dictionary file. - The web engine checks for the \c qtwebengine_dictionaries subdirectory - first in the local directory and if it is not found in the Qt - installation directory: + Qt WebEngine checks for the \c qtwebengine_dictionaries subdirectory + first in the local directory and if it is not found, in the Qt + installation directory. + + On macOS, depending on how Qt WebEngine is configured at build time, there are two possibilities + how spellchecking data is found: + + \list + \li Hunspell dictionaries (default) - .bdic dictionaries are used, just like on other + platforms + \li Native dictionaries - the macOS spellchecking APIs are used (which means the results + will depend on the installed OS dictionaries) + \endlist + + Thus, in the macOS Hunspell case, Qt WebEngine will look in the \e qtwebengine_dictionaries + subdirectory located inside the application bundle \c Resources directory, and also in the + \c Resources directory located inside the Qt framework bundle. + + To summarize, in case of Hunspell usage, the following paths are considered: \list \li QCoreApplication::applicationDirPath()/qtwebengine_dictionaries + or QCoreApplication::applicationDirPath()/../Contents/Resources/qtwebengine_dictionaries + (on macOS) \li [QLibraryInfo::DataPath]/qtwebengine_dictionaries + or path/to/QtWebEngineCore.framework/Resources/qtwebengine_dictionaries (Qt framework + bundle on macOS) \endlist For more information about how to compile \c .bdic dictionaries, see the diff --git a/tools/qmake/mkspecs/features/configure.prf b/tools/qmake/mkspecs/features/configure.prf index 89fa3a242a6cd32dfc7df7293a0e5f8dc5023e2f..ee0cce1b21f66130a1861353db0be3e92698f11a 100644 --- a/tools/qmake/mkspecs/features/configure.prf +++ b/tools/qmake/mkspecs/features/configure.prf @@ -123,6 +123,11 @@ defineTest(finalizeConfigure) { } else { log("AppStore Compliant ............... Not enabled (Default, enable with WEBENGINE_CONFIG+=use_appstore_compliant_code)$${EOL}") } + use?(native_spellchecker) { + log("Native Spellchecker .............. Enabled$${EOL}") + } else { + log("Native Spellchecker .............. Not enabled (Default, enable with WEBENGINE_CONFIG+=use_native_spellchecker)$${EOL}") + } !isMinOSXSDKVersion(10, 10, 3) { log("Force Touch API usage ............ Not enabled (Because the OS X SDK version to be used \"$${WEBENGINE_OSX_SDK_PRODUCT_VERSION}\" is lower than the required \"10.10.3\")$${EOL}") }