1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qdesktopservices.h"
5
6#ifndef QT_NO_DESKTOPSERVICES
7
8#include <qdebug.h>
9
10#include <qstandardpaths.h>
11#include <qhash.h>
12#include <qobject.h>
13#include <qcoreapplication.h>
14#include <private/qguiapplication_p.h>
15#include <qurl.h>
16#include <qmutex.h>
17#include <qpa/qplatformservices.h>
18#include <qpa/qplatformintegration.h>
19#include <qdir.h>
20
21QT_BEGIN_NAMESPACE
22
23class QOpenUrlHandlerRegistry
24{
25public:
26 QOpenUrlHandlerRegistry() = default;
27
28 QRecursiveMutex mutex;
29
30 struct Handler
31 {
32 QObject *receiver;
33 QByteArray name;
34 };
35 typedef QHash<QString, Handler> HandlerHash;
36 HandlerHash handlers;
37};
38
39Q_GLOBAL_STATIC(QOpenUrlHandlerRegistry, handlerRegistry)
40
41/*!
42 \class QDesktopServices
43 \brief The QDesktopServices class provides methods for accessing common desktop services.
44 \since 4.2
45 \ingroup desktop
46 \inmodule QtGui
47
48 Many desktop environments provide services that can be used by applications to
49 perform common tasks, such as opening a web page, in a way that is both consistent
50 and takes into account the user's application preferences.
51
52 This class contains functions that provide simple interfaces to these services
53 that indicate whether they succeeded or failed.
54
55 The openUrl() function is used to open files located at arbitrary URLs in external
56 applications. For URLs that correspond to resources on the local filing system
57 (where the URL scheme is "file"), a suitable application will be used to open the
58 file; otherwise, a web browser will be used to fetch and display the file.
59
60 The user's desktop settings control whether certain executable file types are
61 opened for browsing, or if they are executed instead. Some desktop environments
62 are configured to prevent users from executing files obtained from non-local URLs,
63 or to ask the user's permission before doing so.
64
65 \section1 URL Handlers
66
67 The behavior of the openUrl() function can be customized for individual URL
68 schemes to allow applications to override the default handling behavior for
69 certain types of URLs.
70
71 The dispatch mechanism allows only one custom handler to be used for each URL
72 scheme; this is set using the setUrlHandler() function. Each handler is
73 implemented as a slot which accepts only a single QUrl argument.
74
75 The existing handlers for each scheme can be removed with the
76 unsetUrlHandler() function. This returns the handling behavior for the given
77 scheme to the default behavior.
78
79 This system makes it easy to implement a help system, for example. Help could be
80 provided in labels and text browsers using \uicontrol{help://myapplication/mytopic}
81 URLs, and by registering a handler it becomes possible to display the help text
82 inside the application:
83
84 \snippet code/src_gui_util_qdesktopservices.cpp 0
85 \snippet code/src_gui_util_qdesktopservices.cpp setUrlHandler
86
87 If inside the handler you decide that you can't open the requested
88 URL, you can just call QDesktopServices::openUrl() again with the
89 same argument, and it will try to open the URL using the
90 appropriate mechanism for the user's desktop environment.
91
92 Combined with platform specific settings, the schemes registered by the
93 openUrl() function can also be exposed to other applications, opening up
94 for application deep linking or a very basic URL-based IPC mechanism.
95
96 \sa QSystemTrayIcon, QProcess, QStandardPaths
97*/
98
99/*!
100 Opens the given \a url in the appropriate Web browser for the user's desktop
101 environment, and returns \c true if successful; otherwise returns \c false.
102
103 If the URL is a reference to a local file (i.e., the URL scheme is "file") then
104 it will be opened with a suitable application instead of a Web browser.
105
106 The following example opens a file on the Windows file system residing on a path
107 that contains spaces:
108
109 \snippet code/src_gui_util_qdesktopservices.cpp 2
110
111 If a \c mailto URL is specified, the user's e-mail client will be used to open a
112 composer window containing the options specified in the URL, similar to the way
113 \c mailto links are handled by a Web browser.
114
115 For example, the following URL contains a recipient (\c{user@foo.com}), a
116 subject (\c{Test}), and a message body (\c{Just a test}):
117
118 \snippet code/src_gui_util_qdesktopservices.cpp 1
119
120 \warning Although many e-mail clients can send attachments and are
121 Unicode-aware, the user may have configured their client without these features.
122 Also, certain e-mail clients (e.g., Lotus Notes) have problems with long URLs.
123
124 \warning A return value of \c true indicates that the application has successfully requested
125 the operating system to open the URL in an external application. The external application may
126 still fail to launch or fail to open the requested URL. This result will not be reported back
127 to the application.
128
129 \warning URLs passed to this function on iOS will not load unless their schemes are
130 listed in the \c LSApplicationQueriesSchemes key of the application's Info.plist file.
131 For more information, see the Apple Developer Documentation for
132 \l {iOS: canOpenURL:}{canOpenURL:}.
133 For example, the following lines enable URLs with the HTTPS scheme:
134
135 \snippet code/src_gui_util_qdesktopservices.cpp 3
136
137 \note For Android Nougat (SDK 24) and above, URLs with a \c file scheme
138 are opened using \l {Android: FileProvider}{FileProvider} which tries to obtain
139 a shareable \c content scheme URI first. For that reason, Qt for Android defines
140 a file provider with the authority \c ${applicationId}.qtprovider, with \c applicationId
141 being the app's package name to avoid name conflicts. For more information, also see
142 \l {Android: Setting up file sharing}{Setting up file sharing}.
143
144 \sa setUrlHandler()
145*/
146bool QDesktopServices::openUrl(const QUrl &url)
147{
148 QOpenUrlHandlerRegistry *registry = handlerRegistry();
149 QMutexLocker locker(&registry->mutex);
150 static bool insideOpenUrlHandler = false;
151
152 if (!insideOpenUrlHandler) {
153 QOpenUrlHandlerRegistry::HandlerHash::ConstIterator handler = registry->handlers.constFind(key: url.scheme());
154 if (handler != registry->handlers.constEnd()) {
155 insideOpenUrlHandler = true;
156 bool result = QMetaObject::invokeMethod(obj: handler->receiver, member: handler->name.constData(), c: Qt::DirectConnection, Q_ARG(QUrl, url));
157 insideOpenUrlHandler = false;
158 return result; // ### support bool slot return type
159 }
160 }
161 if (!url.isValid())
162 return false;
163
164 QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
165 if (Q_UNLIKELY(!platformIntegration)) {
166 QCoreApplication *application = QCoreApplication::instance();
167 if (Q_UNLIKELY(!application))
168 qWarning(msg: "QDesktopServices::openUrl: Please instantiate the QGuiApplication object "
169 "first");
170 else if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(application)))
171 qWarning(msg: "QDesktopServices::openUrl: Application is not a GUI application");
172 return false;
173 }
174
175 QPlatformServices *platformServices = platformIntegration->services();
176 if (!platformServices) {
177 qWarning(msg: "The platform plugin does not support services.");
178 return false;
179 }
180 // We only use openDocument if there is no fragment for the URL to
181 // avoid it being lost when using openDocument
182 if (url.isLocalFile() && !url.hasFragment())
183 return platformServices->openDocument(url);
184 return platformServices->openUrl(url);
185}
186
187/*!
188 Sets the handler for the given \a scheme to be the handler \a method provided by
189 the \a receiver object.
190
191 This function provides a way to customize the behavior of openUrl(). If openUrl()
192 is called with a URL with the specified \a scheme then the given \a method on the
193 \a receiver object is called instead of QDesktopServices launching an external
194 application.
195
196 The provided method must be implemented as a slot that only accepts a single QUrl
197 argument.
198
199 \snippet code/src_gui_util_qdesktopservices.cpp 0
200
201 If setUrlHandler() is used to set a new handler for a scheme which already
202 has a handler, the existing handler is simply replaced with the new one.
203 Since QDesktopServices does not take ownership of handlers, no objects are
204 deleted when a handler is replaced.
205
206 Note that the handler will always be called from within the same thread that
207 calls QDesktopServices::openUrl().
208
209 You must call unsetUrlHandler() before destroying the handler object, so
210 the destruction of the handler object does not overlap with concurrent
211 invocations of openUrl() using it.
212
213 \target configuring qdesktopservices url handler on ios and macos
214 \section1 iOS and \macos
215
216 To use this function for receiving data from other apps on iOS/\macos
217 you also need to add the custom scheme to the \c CFBundleURLSchemes
218 list in your Info.plist file:
219
220 \snippet code/src_gui_util_qdesktopservices.cpp 4
221
222 For more information, see the Apple Developer Documentation for
223 \l {iOS: Defining a Custom URL Scheme for Your App}{Defining a Custom URL Scheme for Your App}.
224 \warning It is not possible to claim support for some well known URL schemes, including http and
225 https. This is only allowed for Universal Links.
226
227 To claim support for http and https the above entry in the Info.plist file
228 is not allowed. This is only possible when you add your domain to the
229 Entitlements file:
230
231 \snippet code/src_gui_util_qdesktopservices.cpp 7
232
233 iOS/\macos will search for /.well-known/apple-app-site-association on your domain,
234 when the application is installed. If you want to listen to
235 \c{https://your.domain.com/help?topic=ABCDEF} you need to provide the following
236 content there:
237
238 \snippet code/src_gui_util_qdesktopservices.cpp 8
239
240 For more information, see the Apple Developer Documentation for
241 \l {iOS: Supporting Associated Domains}{Supporting Associated Domains}.
242
243 \target configuring qdesktopservices url handler on android
244 \section1 Android
245
246 To use this function for receiving data from other apps on Android, you
247 need to add one or more intent filter to the \c activity in your app manifest:
248
249 \snippet code/src_gui_util_qdesktopservices.cpp 9
250
251 For more information, see the Android Developer Documentation for
252 \l {Android: Create Deep Links to App Content}{Create Deep Links to App Content}.
253
254 To immediately open the corresponding content in your Android app, without
255 requiring the user to select the app, you need to verify your link. To
256 enable the verification, add an additional parameter to your intent filter:
257
258 \snippet code/src_gui_util_qdesktopservices.cpp 10
259
260 Android will look for \c{https://your.domain.com/.well-known/assetlinks.json},
261 when the application is installed. If you want to listen to
262 \c{https://your.domain.com:1337/help}, you need to provide the following
263 content there:
264
265 \snippet code/src_gui_util_qdesktopservices.cpp 11
266
267 For more information, see the Android Developer Documentation for
268 \l {Android: Verify Android App Links}{Verify Android App Links}.
269
270 \sa openUrl(), unsetUrlHandler()
271*/
272void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, const char *method)
273{
274 QOpenUrlHandlerRegistry *registry = handlerRegistry();
275 QMutexLocker locker(&registry->mutex);
276 if (!receiver) {
277 registry->handlers.remove(key: scheme.toLower());
278 return;
279 }
280 QOpenUrlHandlerRegistry::Handler h;
281 h.receiver = receiver;
282 h.name = method;
283 registry->handlers.insert(key: scheme.toLower(), value: h);
284}
285
286/*!
287 Removes a previously set URL handler for the specified \a scheme.
288
289 Call this function before the handler object that was registered for \a scheme
290 is destroyed, to prevent concurrent openUrl() calls from continuing to call
291 the destroyed handler object.
292
293 \sa setUrlHandler()
294*/
295void QDesktopServices::unsetUrlHandler(const QString &scheme)
296{
297 setUrlHandler(scheme, receiver: nullptr, method: nullptr);
298}
299
300QT_END_NAMESPACE
301
302#endif // QT_NO_DESKTOPSERVICES
303

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/gui/util/qdesktopservices.cpp