[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebKit / UIProcess / API / glib / WebKitGeolocationManager.cpp
1 /*
2  * Copyright (C) 2019 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebKitGeolocationManager.h"
22
23 #include "APIGeolocationProvider.h"
24 #include "GeoclueGeolocationProvider.h"
25 #include "WebGeolocationPosition.h"
26 #include "WebKitGeolocationManagerPrivate.h"
27 #include <glib/gi18n-lib.h>
28 #include <wtf/WallTime.h>
29 #include <wtf/glib/WTFGType.h>
30
31 using namespace WebKit;
32 using namespace WebCore;
33
34 /**
35  * SECTION:WebKitGeolocationManager
36  * @short_description: WebKitGeolocationManager
37  * @title: Geolocation manager
38  * @see_also: #WebKitGeolocationPermissionRequest, #WebKitWebContext
39  *
40  * WebKitGeolocationManager provides API to get the geographical position of the user.
41  * Once a #WebKitGeolocationPermissionRequest is allowed, when WebKit needs to know the
42  * user location #WebKitGeolocationManager::start signal is emitted. If the signal is handled
43  * and returns %TRUE, the application is responsible for providing the position every time it's
44  * updated by calling webkit_geolocation_manager_update_position(). The signal #WebKitGeolocationManager::stop
45  * will be emitted when location updates are no longer needed.
46  *
47  * Since: 2.26
48  */
49
50 enum {
51     PROP_0,
52
53     PROP_ENABLE_HIGH_ACCURACY
54 };
55
56 enum {
57     START,
58     STOP,
59     LAST_SIGNAL
60 };
61
62 struct _WebKitGeolocationPosition {
63     _WebKitGeolocationPosition() = default;
64
65     _WebKitGeolocationPosition(double latitude, double longitude, double accuracy)
66     {
67         position.timestamp = WallTime::now().secondsSinceEpoch().value();
68         position.latitude = latitude;
69         position.longitude = longitude;
70         position.accuracy = accuracy;
71     }
72
73     explicit _WebKitGeolocationPosition(GeolocationPosition&& corePosition)
74         : position(WTFMove(corePosition))
75     {
76     }
77
78     explicit _WebKitGeolocationPosition(const GeolocationPosition& other)
79     {
80         position = other;
81     }
82
83     GeolocationPosition position;
84 };
85
86 /**
87  * WebKitGeolocationPosition:
88  *
89  * WebKitGeolocationPosition is an opaque struct used to provide position updates to a
90  * #WebKitGeolocationManager using webkit_geolocation_manager_update_position().
91  *
92  * Since: 2.26
93  */
94
95 G_DEFINE_BOXED_TYPE(WebKitGeolocationPosition, webkit_geolocation_position, webkit_geolocation_position_copy, webkit_geolocation_position_free)
96
97 /**
98  * webkit_geolocation_position_new:
99  * @latitude: a valid latitude in degrees
100  * @longitude: a valid longitude in degrees
101  * @accuracy: accuracy of location in meters
102  *
103  * Create a new #WebKitGeolocationPosition
104  *
105  * Returns: (transfer full): a newly created #WebKitGeolocationPosition
106  *
107  * Since: 2.26
108  */
109 WebKitGeolocationPosition* webkit_geolocation_position_new(double latitude, double longitude, double accuracy)
110 {
111     auto* position = static_cast<WebKitGeolocationPosition*>(fastMalloc(sizeof(WebKitGeolocationPosition)));
112     new (position) WebKitGeolocationPosition(latitude, longitude, accuracy);
113     return position;
114 }
115
116 /**
117  * webkit_geolocation_position_copy:
118  * @position: a #WebKitGeolocationPosition
119  *
120  * Make a copy of the #WebKitGeolocationPosition
121  *
122  * Returns: (transfer full): a copy of @position
123  *
124  * Since: 2.26
125  */
126 WebKitGeolocationPosition* webkit_geolocation_position_copy(WebKitGeolocationPosition* position)
127 {
128     g_return_val_if_fail(position, nullptr);
129
130     auto* copy = static_cast<WebKitGeolocationPosition*>(fastMalloc(sizeof(WebKitGeolocationPosition)));
131     new (copy) WebKitGeolocationPosition(position->position);
132     return copy;
133 }
134
135 /**
136  * webkit_geolocation_position_free:
137  * @position: a #WebKitGeolocationPosition
138  *
139  * Free the #WebKitGeolocationPosition
140  *
141  * Since: 2.26
142  */
143 void webkit_geolocation_position_free(WebKitGeolocationPosition* position)
144 {
145     g_return_if_fail(position);
146
147     position->~WebKitGeolocationPosition();
148     fastFree(position);
149 }
150
151 /**
152  * webkit_geolocation_position_set_timestamp:
153  * @position: a #WebKitGeolocationPosition
154  * @timestamp: timestamp in seconds since the epoch, or 0 to use current time
155  *
156  * Set the @position timestamp. By default it's the time when the @position was created.
157  *
158  * Since: 2.26
159  */
160 void webkit_geolocation_position_set_timestamp(WebKitGeolocationPosition* position, guint64 timestamp)
161 {
162     g_return_if_fail(position);
163
164     position->position.timestamp = timestamp ? static_cast<double>(timestamp) : WallTime::now().secondsSinceEpoch().value();
165 }
166
167 /**
168  * webkit_geolocation_position_set_altitude:
169  * @position: a #WebKitGeolocationPosition
170  * @altitude: altitude in meters
171  *
172  * Set the @position altitude
173  *
174  * Since: 2.26
175  */
176 void webkit_geolocation_position_set_altitude(WebKitGeolocationPosition* position, double altitude)
177 {
178     g_return_if_fail(position);
179
180     position->position.altitude = altitude;
181 }
182
183 /**
184  * webkit_geolocation_position_set_altitude_accuracy:
185  * @position: a #WebKitGeolocationPosition
186  * @altitude_accuracy: accuracy of position altitude in meters
187  *
188  * Set the accuracy of @position altitude
189  *
190  * Since: 2.26
191  */
192 void webkit_geolocation_position_set_altitude_accuracy(WebKitGeolocationPosition* position, double altitudeAccuracy)
193 {
194     g_return_if_fail(position);
195
196     position->position.altitudeAccuracy = altitudeAccuracy;
197 }
198
199 /**
200  * webkit_geolocation_position_set_heading:
201  * @position: a #WebKitGeolocationPosition
202  * @heading: heading in degrees
203  *
204  * Set the @position heading, as a positive angle between the direction of movement and the North
205  * direction, in clockwise direction.
206  *
207  * Since: 2.26
208  */
209 void webkit_geolocation_position_set_heading(WebKitGeolocationPosition* position, double heading)
210 {
211     g_return_if_fail(position);
212
213     position->position.heading = heading;
214 }
215
216 /**
217  * webkit_geolocation_position_set_speed:
218  * @position: a #WebKitGeolocationPosition
219  * @speed: speed in meters per second
220  *
221  * Set the @position speed
222  *
223  * Since: 2.26
224  */
225 void webkit_geolocation_position_set_speed(WebKitGeolocationPosition* position, double speed)
226 {
227     g_return_if_fail(position);
228
229     position->position.speed = speed;
230 }
231
232 struct _WebKitGeolocationManagerPrivate {
233     RefPtr<WebGeolocationManagerProxy> manager;
234     bool highAccuracyEnabled;
235     std::unique_ptr<GeoclueGeolocationProvider> geoclueProvider;
236 };
237
238 static guint signals[LAST_SIGNAL] = { 0, };
239
240 WEBKIT_DEFINE_TYPE(WebKitGeolocationManager, webkit_geolocation_manager, G_TYPE_OBJECT)
241
242 static void webkitGeolocationManagerStart(WebKitGeolocationManager* manager)
243 {
244     gboolean returnValue;
245     g_signal_emit(manager, signals[START], 0, &returnValue);
246     if (returnValue) {
247         manager->priv->geoclueProvider = nullptr;
248         return;
249     }
250
251     if (!manager->priv->geoclueProvider) {
252         manager->priv->geoclueProvider = makeUnique<GeoclueGeolocationProvider>();
253         manager->priv->geoclueProvider->setEnableHighAccuracy(manager->priv->highAccuracyEnabled);
254     }
255     manager->priv->geoclueProvider->start([manager](GeolocationPosition&& corePosition, Optional<CString> error) {
256         if (error) {
257             webkit_geolocation_manager_failed(manager, error->data());
258             return;
259         }
260
261         WebKitGeolocationPosition position(WTFMove(corePosition));
262         webkit_geolocation_manager_update_position(manager, &position);
263     });
264 }
265
266 static void webkitGeolocationManagerStop(WebKitGeolocationManager* manager)
267 {
268     g_signal_emit(manager, signals[STOP], 0, nullptr);
269
270     if (manager->priv->geoclueProvider)
271         manager->priv->geoclueProvider->stop();
272 }
273
274 static void webkitGeolocationManagerSetEnableHighAccuracy(WebKitGeolocationManager* manager, bool enabled)
275 {
276     if (manager->priv->highAccuracyEnabled == enabled)
277         return;
278
279     manager->priv->highAccuracyEnabled = enabled;
280     g_object_notify(G_OBJECT(manager), "enable-high-accuracy");
281     if (manager->priv->geoclueProvider)
282         manager->priv->geoclueProvider->setEnableHighAccuracy(enabled);
283 }
284
285 class GeolocationProvider final : public API::GeolocationProvider {
286 public:
287     explicit GeolocationProvider(WebKitGeolocationManager* manager)
288         : m_manager(manager)
289     {
290     }
291
292 private:
293     void startUpdating(WebGeolocationManagerProxy&) override
294     {
295         webkitGeolocationManagerStart(m_manager);
296     }
297
298     void stopUpdating(WebGeolocationManagerProxy&) override
299     {
300         webkitGeolocationManagerStop(m_manager);
301     }
302
303     void setEnableHighAccuracy(WebGeolocationManagerProxy&, bool enabled) override
304     {
305         webkitGeolocationManagerSetEnableHighAccuracy(m_manager, enabled);
306     }
307
308     WebKitGeolocationManager* m_manager;
309 };
310
311 WebKitGeolocationManager* webkitGeolocationManagerCreate(WebGeolocationManagerProxy* proxy)
312 {
313     auto* manager = WEBKIT_GEOLOCATION_MANAGER(g_object_new(WEBKIT_TYPE_GEOLOCATION_MANAGER, nullptr));
314     manager->priv->manager = proxy;
315     proxy->setProvider(makeUnique<GeolocationProvider>(manager));
316     return manager;
317 }
318
319 static void webkitGeolocationManagerGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
320 {
321     WebKitGeolocationManager* manager = WEBKIT_GEOLOCATION_MANAGER(object);
322
323     switch (propId) {
324     case PROP_ENABLE_HIGH_ACCURACY:
325         g_value_set_boolean(value, webkit_geolocation_manager_get_enable_high_accuracy(manager));
326         break;
327     default:
328         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
329     }
330 }
331
332 static void webkit_geolocation_manager_class_init(WebKitGeolocationManagerClass* geolocationManagerClass)
333 {
334     GObjectClass* gObjectClass = G_OBJECT_CLASS(geolocationManagerClass);
335     gObjectClass->get_property = webkitGeolocationManagerGetProperty;
336
337     /**
338      * WebKitGeolocationManager:enable-high-accuracy:
339      *
340      * Whether high accuracy is enabled. This is a read-only property that will be
341      * set to %TRUE when a #WebKitGeolocationManager needs to get accurate position updates.
342      * You can connect to notify::enable-high-accuracy signal to monitor it.
343      *
344      * Since: 2.26
345      */
346     g_object_class_install_property(
347         gObjectClass,
348         PROP_ENABLE_HIGH_ACCURACY,
349         g_param_spec_boolean(
350             "enable-high-accuracy",
351             _("Enable high accuracy"),
352             _("Whether high accuracy is enabled"),
353             FALSE,
354             WEBKIT_PARAM_READABLE));
355
356     /**
357      * WebKitGeolocationManager::start:
358      * @manager: the #WebKitGeolocationManager on which the signal is emitted
359      *
360      * The signal is emitted to notify that @manager needs to start receiving
361      * position updates. After this signal is emitted the user should provide
362      * the updates using webkit_geolocation_manager_update_position() every time
363      * the position changes, or use webkit_geolocation_manager_failed() in case
364      * it isn't possible to determine the current position.
365      *
366      * If the signal is not handled, WebKit will try to determine the position
367      * using GeoClue if available.
368      *
369      * Returns: %TRUE to stop other handlers from being invoked for the event.
370      *    %FALSE to propagate the event further.
371      *
372      * Since: 2.26
373      */
374     signals[START] = g_signal_new(
375         "start",
376         G_TYPE_FROM_CLASS(geolocationManagerClass),
377         G_SIGNAL_RUN_LAST,
378         0,
379         g_signal_accumulator_true_handled, nullptr,
380         g_cclosure_marshal_generic,
381         G_TYPE_BOOLEAN, 0);
382
383     /**
384      * WebKitGeolocationManager::stop:
385      * @manager: the #WebKitGeolocationManager on which the signal is emitted
386      *
387      * The signal is emitted to notify that @manager doesn't need to receive
388      * position updates anymore.
389      *
390      * Since: 2.26
391      */
392     signals[STOP] = g_signal_new(
393         "stop",
394         G_TYPE_FROM_CLASS(geolocationManagerClass),
395         G_SIGNAL_RUN_LAST,
396         0,
397         nullptr, nullptr,
398         g_cclosure_marshal_generic,
399         G_TYPE_NONE, 0);
400 }
401
402 /**
403  * webkit_geolocation_manager_update_position:
404  * @manager: a #WebKitGeolocationManager
405  * @position: a #WebKitGeolocationPosition
406  *
407  * Notify @manager that position has been updated to @position.
408  *
409  * Since: 2.26
410  */
411 void webkit_geolocation_manager_update_position(WebKitGeolocationManager* manager, WebKitGeolocationPosition* position)
412 {
413     g_return_if_fail(WEBKIT_IS_GEOLOCATION_MANAGER(manager));
414     g_return_if_fail(position);
415
416     GeolocationPosition corePosition = position->position;
417     auto wkPosition = WebGeolocationPosition::create(WTFMove(corePosition));
418     manager->priv->manager->providerDidChangePosition(wkPosition.ptr());
419 }
420
421 /**
422  * webkit_geolocation_manager_failed:
423  * @manager: a #WebKitGeolocationManager
424  * @error_message: the error message
425  *
426  * Notify @manager that determining the position failed.
427  *
428  * Since: 2.26
429  */
430 void webkit_geolocation_manager_failed(WebKitGeolocationManager* manager, const char* errorMessage)
431 {
432     g_return_if_fail(WEBKIT_IS_GEOLOCATION_MANAGER(manager));
433
434     manager->priv->manager->providerDidFailToDeterminePosition(String::fromUTF8(errorMessage));
435 }
436
437 /**
438  * webkit_geolocation_manager_get_enable_high_accuracy:
439  * @manager: a #WebKitGeolocationManager
440  *
441  * Get whether high accuracy is enabled.
442  *
443  * Since: 2.26
444  */
445 gboolean webkit_geolocation_manager_get_enable_high_accuracy(WebKitGeolocationManager* manager)
446 {
447     g_return_val_if_fail(WEBKIT_IS_GEOLOCATION_MANAGER(manager), FALSE);
448
449     return manager->priv->highAccuracyEnabled;
450 }