blob: ea8d7f03dd43c5d7f4e596267220db0c351eb71b [file] [log] [blame]
/*
* Copyright (C) 2019 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2,1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "TestMain.h"
#include "WebViewTest.h"
#include <wtf/WallTime.h>
class GeolocationTest: public WebViewTest {
public:
MAKE_GLIB_TEST_FIXTURE(GeolocationTest);
struct Position {
double timestamp { std::numeric_limits<double>::quiet_NaN() };
double latitude { std::numeric_limits<double>::quiet_NaN() };
double longitude { std::numeric_limits<double>::quiet_NaN() };
double accuracy { std::numeric_limits<double>::quiet_NaN() };
std::optional<double> altitude;
std::optional<double> altitudeAccuracy;
std::optional<double> heading;
std::optional<double> speed;
};
static gboolean startCallback(WebKitGeolocationManager* manager, GeolocationTest* test)
{
g_assert_true(test->m_manager == manager);
test->start();
return TRUE;
}
static void stopCallback(WebKitGeolocationManager* manager, GeolocationTest* test)
{
g_assert_true(test->m_manager == manager);
test->stop();
}
static gboolean permissionRequested(WebKitWebView*, WebKitPermissionRequest* request, GeolocationTest* test)
{
g_assert_true(WEBKIT_IS_PERMISSION_REQUEST(request));
test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(request)) {
webkit_permission_request_allow(request);
return TRUE;
}
return FALSE;
}
GeolocationTest()
: m_manager(webkit_web_context_get_geolocation_manager(m_webContext.get()))
{
g_assert_true(WEBKIT_IS_GEOLOCATION_MANAGER(m_manager));
assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_manager));
g_signal_connect(m_manager, "start", G_CALLBACK(startCallback), this);
g_signal_connect(m_manager, "stop", G_CALLBACK(stopCallback), this);
g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequested), this);
}
~GeolocationTest()
{
if (m_checkPosition)
webkit_geolocation_position_free(m_checkPosition);
g_signal_handlers_disconnect_matched(m_manager, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
}
void start()
{
g_assert_false(m_updating);
m_updating = true;
if (m_errorMessage) {
g_assert_false(m_checkPosition);
webkit_geolocation_manager_failed(m_manager, m_errorMessage.get());
}
if (m_checkPosition) {
g_assert_false(m_errorMessage.get());
webkit_geolocation_manager_update_position(m_manager, m_checkPosition);
}
g_main_loop_quit(m_mainLoop);
}
void stop()
{
g_assert_true(m_updating);
m_updating = false;
if (m_watching)
g_main_loop_quit(m_mainLoop);
}
Position lastPosition()
{
GUniqueOutPtr<GError> error;
auto* javascriptResult = runJavaScriptAndWaitUntilFinished("position", &error.outPtr());
g_assert_nonnull(javascriptResult);
g_assert_no_error(error.get());
auto* jsPosition = webkit_javascript_result_get_js_value(javascriptResult);
g_assert_true(jsc_value_is_object(jsPosition));
Position result;
GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(jsPosition, "latitude"));
g_assert_true(jsc_value_is_number(value.get()));
result.latitude = jsc_value_to_double(value.get());
value = adoptGRef(jsc_value_object_get_property(jsPosition, "longitude"));
g_assert_true(jsc_value_is_number(value.get()));
result.longitude = jsc_value_to_double(value.get());
value = adoptGRef(jsc_value_object_get_property(jsPosition, "accuracy"));
g_assert_true(jsc_value_is_number(value.get()));
result.accuracy = jsc_value_to_double(value.get());
value = adoptGRef(jsc_value_object_get_property(jsPosition, "altitude"));
if (!jsc_value_is_null(value.get())) {
g_assert_true(jsc_value_is_number(value.get()));
result.altitude = jsc_value_to_double(value.get());
}
value = adoptGRef(jsc_value_object_get_property(jsPosition, "altitudeAccuracy"));
if (!jsc_value_is_null(value.get())) {
g_assert_true(jsc_value_is_number(value.get()));
result.altitudeAccuracy = jsc_value_to_double(value.get());
}
value = adoptGRef(jsc_value_object_get_property(jsPosition, "heading"));
if (!jsc_value_is_null(value.get())) {
g_assert_true(jsc_value_is_number(value.get()));
result.heading = jsc_value_to_double(value.get());
}
value = adoptGRef(jsc_value_object_get_property(jsPosition, "speed"));
if (!jsc_value_is_null(value.get())) {
g_assert_true(jsc_value_is_number(value.get()));
result.speed = jsc_value_to_double(value.get());
}
value = adoptGRef(jsc_value_object_get_property(jsPosition, "timestamp"));
g_assert_true(jsc_value_is_number(value.get()));
result.timestamp = jsc_value_to_double(value.get());
return result;
}
Position checkCurrentPosition(WebKitGeolocationPosition* position, bool enableHighAccuracy = false)
{
g_clear_pointer(&m_checkPosition, webkit_geolocation_position_free);
m_checkPosition = position;
static const char getCurrentPosition[] =
"var position = null;\n"
"options = { enableHighAccuracy: %s }\n"
"navigator.geolocation.getCurrentPosition(function(p) {\n"
" position = { };\n"
" position.latitude = p.coords.latitude;\n"
" position.longitude = p.coords.longitude;\n"
" position.accuracy = p.coords.accuracy;\n"
" position.altitude = p.coords.altitude;\n"
" position.altitudeAccuracy = p.coords.altitudeAccuracy;\n"
" position.heading = p.coords.heading;\n"
" position.speed = p.coords.speed;\n"
" position.timestamp = p.timestamp;\n"
"}, undefined, options);";
GUniquePtr<char> script(g_strdup_printf(getCurrentPosition, enableHighAccuracy ? "true" : "false"));
GUniqueOutPtr<GError> error;
runJavaScriptAndWaitUntilFinished(script.get(), &error.outPtr());
g_assert_no_error(error.get());
g_main_loop_run(m_mainLoop);
g_clear_pointer(&m_checkPosition, webkit_geolocation_position_free);
g_assert_true(webkit_geolocation_manager_get_enable_high_accuracy(m_manager) == enableHighAccuracy);
return lastPosition();
}
GUniquePtr<char> checkFailedToDeterminePosition(const char* errorMessage)
{
m_errorMessage.reset(g_strdup(errorMessage));
static const char getCurrentPosition[] =
"var error;\n"
"navigator.geolocation.getCurrentPosition(\n"
" function(p) { error = null; },\n"
" function(e) { error = e.message.toString(); }\n"
");";
GUniqueOutPtr<GError> error;
auto* javascriptResult = runJavaScriptAndWaitUntilFinished(getCurrentPosition, &error.outPtr());
g_assert_no_error(error.get());
g_main_loop_run(m_mainLoop);
m_errorMessage = nullptr;
javascriptResult = runJavaScriptAndWaitUntilFinished("error", &error.outPtr());
g_assert_nonnull(javascriptResult);
g_assert_no_error(error.get());
auto* jsErrorMessage = webkit_javascript_result_get_js_value(javascriptResult);
g_assert_true(jsc_value_is_string(jsErrorMessage));
return GUniquePtr<char>(jsc_value_to_string(jsErrorMessage));
}
void startWatch()
{
g_clear_pointer(&m_checkPosition, webkit_geolocation_position_free);
m_errorMessage = nullptr;
m_watching = true;
static const char watchPosition[] =
"var position = null;\n"
"var watchID = navigator.geolocation.watchPosition(function(p) {\n"
" position = { };\n"
" position.latitude = p.coords.latitude;\n"
" position.longitude = p.coords.longitude;\n"
" position.accuracy = p.coords.accuracy;\n"
" position.altitude = p.coords.altitude;\n"
" position.altitudeAccuracy = p.coords.altitudeAccuracy;\n"
" position.heading = p.coords.heading;\n"
" position.speed = p.coords.speed;\n"
" position.timestamp = p.timestamp;\n"
"});";
GUniqueOutPtr<GError> error;
auto* javascriptResult = runJavaScriptAndWaitUntilFinished(watchPosition, &error.outPtr());
g_assert_nonnull(javascriptResult);
g_assert_no_error(error.get());
g_main_loop_run(m_mainLoop);
}
void stopWatch()
{
GUniqueOutPtr<GError> error;
runJavaScriptAndWaitUntilFinished("navigator.geolocation.clearWatch(watchID)", &error.outPtr());
g_assert_no_error(error.get());
g_main_loop_run(m_mainLoop);
m_watching = false;
}
WebKitGeolocationManager* m_manager;
bool m_updating { false };
bool m_watching { false };
WebKitGeolocationPosition* m_checkPosition { nullptr };
GUniquePtr<char> m_errorMessage;
};
static void testGeolocationManagerCurrentPosition(GeolocationTest* test, gconstpointer)
{
test->showInWindow();
test->loadHtml("<html><body></body></html>", "https://foo.com/bar");
test->waitUntilLoadFinished();
WebKitGeolocationPosition* position = webkit_geolocation_position_new(37.1760783, -3.59033, 17);
g_assert_false(test->m_updating);
auto result = test->checkCurrentPosition(position);
g_assert_false(test->m_updating);
g_assert_cmpfloat(result.latitude, ==, 37.1760783);
g_assert_cmpfloat(result.longitude, ==, -3.59033);
g_assert_cmpfloat(result.accuracy, ==, 17);
g_assert_false(result.altitude);
g_assert_false(result.altitudeAccuracy);
g_assert_false(result.heading);
g_assert_false(result.speed);
g_assert_cmpfloat(result.timestamp, >, 0);
g_assert_cmpfloat(result.timestamp / 1000., <, WallTime::now().secondsSinceEpoch().value());
position = webkit_geolocation_position_new(27.986065, 86.922623, 0);
auto timestamp = WallTime::now().secondsSinceEpoch();
webkit_geolocation_position_set_timestamp(position, timestamp.value());
webkit_geolocation_position_set_altitude(position, 8495);
webkit_geolocation_position_set_altitude_accuracy(position, 0);
webkit_geolocation_position_set_heading(position, 100.55);
webkit_geolocation_position_set_speed(position, 0.05);
g_assert_false(test->m_updating);
result = test->checkCurrentPosition(position, true);
g_assert_false(test->m_updating);
g_assert_cmpfloat(result.latitude, ==, 27.986065);
g_assert_cmpfloat(result.longitude, ==, 86.922623);
g_assert_cmpfloat(result.accuracy, ==, 0);
g_assert_true(result.altitude);
g_assert_cmpfloat(result.altitude.value(), ==, 8495);
g_assert_true(result.altitudeAccuracy);
g_assert_cmpfloat(result.altitudeAccuracy.value(), ==, 0);
g_assert_true(result.heading);
g_assert_cmpfloat(result.heading.value(), ==, 100.55);
g_assert_true(result.speed);
g_assert_cmpfloat(result.speed.value(), ==, 0.05);
g_assert_cmpfloat_with_epsilon(result.timestamp / 1000., timestamp.value(), 1);
auto errorMessage = test->checkFailedToDeterminePosition("GPS is not active");
g_assert_cmpstr(errorMessage.get(), ==, "GPS is not active");
}
static void testGeolocationManagerWatchPosition(GeolocationTest* test, gconstpointer)
{
test->showInWindow();
test->loadHtml("<html><body></body></html>", "https://foo.com/bar");
test->waitUntilLoadFinished();
test->startWatch();
g_assert_true(test->m_updating);
WebKitGeolocationPosition* position = webkit_geolocation_position_new(37.1760783, -3.59033, 17);
webkit_geolocation_manager_update_position(test->m_manager, position);
webkit_geolocation_position_free(position);
auto result = test->lastPosition();
g_assert_cmpfloat(result.latitude, ==, 37.1760783);
g_assert_cmpfloat(result.longitude, ==, -3.59033);
g_assert_cmpfloat(result.accuracy, ==, 17);
position = webkit_geolocation_position_new(38.1770783, -3.60033, 17);
webkit_geolocation_manager_update_position(test->m_manager, position);
webkit_geolocation_position_free(position);
result = test->lastPosition();
g_assert_cmpfloat(result.latitude, ==, 38.1770783);
g_assert_cmpfloat(result.longitude, ==, -3.60033);
g_assert_cmpfloat(result.accuracy, ==, 17);
test->stopWatch();
g_assert_false(test->m_updating);
}
void beforeAll()
{
GeolocationTest::add("WebKitGeolocationManager", "current-position", testGeolocationManagerCurrentPosition);
GeolocationTest::add("WebKitGeolocationManager", "watch-position", testGeolocationManagerWatchPosition);
}
void afterAll()
{
}