Skip to content

Commit d43da44

Browse files
committed
LibWeb: Request geolocation positions from Page
Implement the Geolocation acquisition path by bridging the spec algorithm through Page so WebContent can ask the browser process for position data. One-shot requests are coalesced onto the active native request, while watch requests remain associated with their watch ID. This also wires emulated position data into top-level traversables so manual settings and tests can drive both getCurrentPosition() and watchPosition() without a native provider.
1 parent 48d3b1b commit d43da44

17 files changed

Lines changed: 489 additions & 52 deletions

Libraries/LibWeb/Geolocation/Geolocation.cpp

Lines changed: 222 additions & 45 deletions
Large diffs are not rendered by default.

Libraries/LibWeb/Geolocation/Geolocation.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,20 @@
66

77
#pragma once
88

9+
#include <AK/HashMap.h>
10+
#include <LibWeb/Bindings/Geolocation.h>
911
#include <LibWeb/Bindings/PlatformObject.h>
1012
#include <LibWeb/Geolocation/GeolocationPositionError.h>
13+
#include <LibWeb/HighResolutionTime/EpochTimeStamp.h>
1114
#include <LibWeb/Platform/Timer.h>
1215
#include <LibWeb/WebIDL/Types.h>
1316

17+
namespace Web::HTML {
18+
19+
class LocalTraversableNavigable;
20+
21+
}
22+
1423
namespace Web::Geolocation {
1524

1625
// https://w3c.github.io/geolocation/#dfn-emulated-position-data
@@ -27,19 +36,32 @@ class Geolocation : public Bindings::PlatformObject {
2736
void clear_watch(WebIDL::Long);
2837

2938
private:
39+
struct WatchPositionData {
40+
GC::Ref<WebIDL::CallbackType> success_callback;
41+
GC::Ptr<WebIDL::CallbackType> error_callback;
42+
Bindings::PositionOptions options;
43+
Optional<u64> emulated_position_data_observer_id {};
44+
};
45+
3046
Geolocation(JS::Realm&);
3147

3248
virtual void initialize(JS::Realm&) override;
3349
virtual void visit_edges(Visitor&) override;
3450

3551
void acquire_a_position(GC::Ref<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>, Bindings::PositionOptions const&, Optional<WebIDL::UnsignedLong>);
52+
void acquire_position_from_page(GC::Ref<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>, Bindings::PositionOptions const&, Optional<WebIDL::UnsignedLong>, HighResolutionTime::EpochTimeStamp);
3653
void call_back_with_error(GC::Ptr<WebIDL::CallbackType>, GeolocationPositionError::ErrorCode) const;
3754
EmulatedPositionData get_emulated_position_data() const;
55+
void remove_watch_id(WebIDL::UnsignedLong);
56+
GC::Ptr<HTML::LocalTraversableNavigable> top_level_traversable() const;
3857
void request_a_position(GC::Ref<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>, Bindings::PositionOptions const&, Optional<WebIDL::UnsignedLong> = {});
3958
void run_in_parallel_when_document_is_visible(DOM::Document&, GC::Ref<GC::Function<void()>>);
59+
void unregister_watch_position_observer(WebIDL::UnsignedLong);
4060

4161
// https://w3c.github.io/geolocation/#dfn-watchids
4262
HashTable<WebIDL::UnsignedLong> m_watch_ids;
63+
HashMap<WebIDL::UnsignedLong, WatchPositionData> m_watch_position_data;
64+
HashMap<WebIDL::UnsignedLong, u64> m_pending_watch_position_request_ids;
4365

4466
// https://w3c.github.io/geolocation/#dfn-cachedposition
4567
GC::Ptr<GeolocationPosition> m_cached_position;

Libraries/LibWeb/Geolocation/GeolocationCoordinates.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ struct CoordinatesData {
1414
double accuracy { 0.0 };
1515
double latitude { 0.0 };
1616
double longitude { 0.0 };
17-
Optional<double> altitude;
18-
Optional<double> altitude_accuracy;
19-
Optional<double> heading;
20-
Optional<double> speed;
17+
Optional<double> altitude {};
18+
Optional<double> altitude_accuracy {};
19+
Optional<double> heading {};
20+
Optional<double> speed {};
2121
};
2222

2323
// https://w3c.github.io/geolocation/#coordinates_interface

Libraries/LibWeb/HTML/LocalTraversableNavigable.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ LocalTraversableNavigable::~LocalTraversableNavigable() = default;
5353
void LocalTraversableNavigable::visit_edges(Cell::Visitor& visitor)
5454
{
5555
Base::visit_edges(visitor);
56-
if (m_emulated_position_data.has<GC::Ref<Geolocation::GeolocationCoordinates>>())
57-
visitor.visit(m_emulated_position_data.get<GC::Ref<Geolocation::GeolocationCoordinates>>());
56+
visitor.visit(m_emulated_position_data);
57+
visitor.visit(m_emulated_position_data_observers);
5858
visitor.visit(m_session_history_traversal_queue);
5959
visitor.visit(m_storage_shed);
6060
visitor.visit(m_apply_history_step_state);
@@ -2671,6 +2671,34 @@ void LocalTraversableNavigable::set_emulated_position_data(Geolocation::Emulated
26712671
{
26722672
VERIFY(is_top_level_traversable());
26732673
m_emulated_position_data = data;
2674+
2675+
Vector<GC::Root<GC::Function<void()>>> observers;
2676+
for (auto& observer : m_emulated_position_data_observers)
2677+
observers.append(observer.value);
2678+
for (auto& observer : observers)
2679+
observer->function()();
2680+
}
2681+
2682+
void LocalTraversableNavigable::set_emulated_position_data(Geolocation::CoordinatesData coordinates_data)
2683+
{
2684+
VERIFY(is_top_level_traversable());
2685+
auto& realm = active_document()->realm();
2686+
auto coords = realm.create<Geolocation::GeolocationCoordinates>(realm, move(coordinates_data));
2687+
set_emulated_position_data(coords);
2688+
}
2689+
2690+
u64 LocalTraversableNavigable::register_emulated_position_data_observer(GC::Ref<GC::Function<void()>> observer)
2691+
{
2692+
VERIFY(is_top_level_traversable());
2693+
auto observer_id = m_next_emulated_position_data_observer_id++;
2694+
m_emulated_position_data_observers.set(observer_id, observer);
2695+
return observer_id;
2696+
}
2697+
2698+
void LocalTraversableNavigable::unregister_emulated_position_data_observer(u64 observer_id)
2699+
{
2700+
VERIFY(is_top_level_traversable());
2701+
m_emulated_position_data_observers.remove(observer_id);
26742702
}
26752703

26762704
void LocalTraversableNavigable::process_screenshot_requests()

Libraries/LibWeb/HTML/LocalTraversableNavigable.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#pragma once
1010

11+
#include <AK/HashMap.h>
1112
#include <AK/Utf16String.h>
1213
#include <AK/Vector.h>
1314
#include <LibWeb/Bindings/NavigationType.h>
@@ -133,6 +134,9 @@ class WEB_API LocalTraversableNavigable final : public LocalNavigable {
133134
// https://w3c.github.io/geolocation/#dfn-emulated-position-data
134135
Geolocation::EmulatedPositionData const& emulated_position_data() const;
135136
void set_emulated_position_data(Geolocation::EmulatedPositionData data);
137+
void set_emulated_position_data(Geolocation::CoordinatesData);
138+
u64 register_emulated_position_data_observer(GC::Ref<GC::Function<void()>>);
139+
void unregister_emulated_position_data_observer(u64 observer_id);
136140

137141
void process_screenshot_requests();
138142
void queue_screenshot_task(Optional<UniqueNodeID> node_id)
@@ -248,6 +252,8 @@ class WEB_API LocalTraversableNavigable final : public LocalNavigable {
248252

249253
// https://w3c.github.io/geolocation/#dfn-emulated-position-data
250254
Geolocation::EmulatedPositionData m_emulated_position_data;
255+
HashMap<u64, GC::Ref<GC::Function<void()>>> m_emulated_position_data_observers;
256+
u64 m_next_emulated_position_data_observer_id { 0 };
251257

252258
struct ScreenshotTask {
253259
Optional<Web::UniqueNodeID> node_id;

Libraries/LibWeb/Internals/Internals.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,15 @@ WebIDL::ExceptionOr<void> Internals::set_environments_top_level_url(Utf16String
881881
return {};
882882
}
883883

884+
void Internals::set_geolocation_emulated_position(double latitude, double longitude, double accuracy)
885+
{
886+
as<HTML::LocalTraversableNavigable>(*window().navigable()->top_level_traversable()).set_emulated_position_data(Geolocation::CoordinatesData {
887+
.accuracy = accuracy,
888+
.latitude = latitude,
889+
.longitude = longitude,
890+
});
891+
}
892+
884893
JS::Object* Internals::get_style_invalidation_counters()
885894
{
886895
auto const& counters = window().associated_document().style_invalidation_counters();

Libraries/LibWeb/Internals/Internals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class WEB_API Internals final : public InternalsBase {
141141

142142
void clear_element(HTML::HTMLElement&);
143143
WebIDL::ExceptionOr<void> set_environments_top_level_url(Utf16String const& url);
144+
void set_geolocation_emulated_position(double latitude, double longitude, double accuracy);
144145

145146
JS::Object* get_style_invalidation_counters();
146147
void reset_style_invalidation_counters();

Libraries/LibWeb/Internals/Internals.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ interface Internals {
125125
undefined handleSDLInputEvents();
126126

127127
undefined setEnvironmentsTopLevelURL(Utf16USVString url);
128+
undefined setGeolocationEmulatedPosition(double latitude, double longitude, optional double accuracy = 100);
128129

129130
InternalGamepad connectVirtualGamepad();
130131

Libraries/LibWeb/Page/Page.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ void Page::visit_edges(JS::Cell::Visitor& visitor)
9999
visitor.visit(m_window_rect_observer);
100100
visitor.visit(m_on_pending_dialog_closed);
101101
visitor.visit(m_pending_clipboard_requests);
102+
for (auto const& request : m_pending_geolocation_requests)
103+
visitor.visit(request.value.callback);
102104
m_pending_fullscreen_operations.for_each([&](auto const& operation) {
103105
operation.visit([&](PendingFullscreenEnter const& enter_operation) {
104106
visitor.visit(enter_operation.element);
@@ -677,6 +679,86 @@ void Page::retrieved_clipboard_entries(u64 request_id, Vector<Clipboard::SystemC
677679
(*request)->function()(move(items));
678680
}
679681

682+
u64 Page::request_geolocation_position(GeolocationPositionCallback callback, GeolocationRequestType type)
683+
{
684+
// AD-HOC: This is the browser-process bridge for the Geolocation spec's "try to acquire position data from the
685+
// underlying system" step.
686+
auto request_id = m_next_geolocation_request_id++;
687+
m_pending_geolocation_requests.set(request_id, PendingGeolocationRequest {
688+
.callback = callback,
689+
.type = type,
690+
});
691+
692+
if (type == GeolocationRequestType::Watch) {
693+
client().page_did_start_geolocation_position_watch(request_id);
694+
return request_id;
695+
}
696+
697+
if (!m_active_geolocation_request_id.has_value()) {
698+
m_active_geolocation_request_id = request_id;
699+
client().page_did_request_geolocation_position(request_id);
700+
}
701+
702+
return request_id;
703+
}
704+
705+
void Page::cancel_geolocation_position_request(u64 request_id)
706+
{
707+
auto request = m_pending_geolocation_requests.take(request_id);
708+
if (!request.has_value())
709+
return;
710+
711+
if (request->type == GeolocationRequestType::Watch) {
712+
client().page_did_stop_geolocation_position_watch(request_id);
713+
return;
714+
}
715+
716+
if (m_active_geolocation_request_id != request_id)
717+
return;
718+
719+
m_active_geolocation_request_id = {};
720+
for (auto const& entry : m_pending_geolocation_requests) {
721+
if (entry.value.type == GeolocationRequestType::OneShot) {
722+
m_active_geolocation_request_id = entry.key;
723+
client().page_did_request_geolocation_position(entry.key);
724+
return;
725+
}
726+
}
727+
}
728+
729+
void Page::receive_geolocation_position(u64 request_id, Optional<Geolocation::CoordinatesData> coordinates, Optional<Geolocation::GeolocationPositionError::ErrorCode> error)
730+
{
731+
auto request = m_pending_geolocation_requests.get(request_id);
732+
if (!request.has_value())
733+
return;
734+
735+
if (request->type == GeolocationRequestType::Watch) {
736+
auto callback = request->callback;
737+
callback->function()(coordinates, error);
738+
return;
739+
}
740+
741+
if (m_active_geolocation_request_id != request_id)
742+
return;
743+
744+
m_active_geolocation_request_id = {};
745+
746+
Vector<GeolocationPositionCallback> callbacks;
747+
Vector<u64> completed_request_ids;
748+
for (auto const& entry : m_pending_geolocation_requests) {
749+
if (entry.value.type != GeolocationRequestType::OneShot)
750+
continue;
751+
callbacks.append(entry.value.callback);
752+
completed_request_ids.append(entry.key);
753+
}
754+
755+
for (auto completed_request_id : completed_request_ids)
756+
m_pending_geolocation_requests.remove(completed_request_id);
757+
758+
for (auto const& callback : callbacks)
759+
callback->function()(coordinates, error);
760+
}
761+
680762
void Page::register_media_element(Badge<HTML::HTMLMediaElement>, UniqueNodeID media_id)
681763
{
682764
m_media_elements.append(media_id);

Libraries/LibWeb/Page/Page.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
#include <LibWeb/DOM/RequestFullscreenError.h>
4444
#include <LibWeb/Export.h>
4545
#include <LibWeb/Forward.h>
46+
#include <LibWeb/Geolocation/GeolocationCoordinates.h>
47+
#include <LibWeb/Geolocation/GeolocationPositionError.h>
4648
#include <LibWeb/HTML/ActivateTab.h>
4749
#include <LibWeb/HTML/AudioPlayState.h>
4850
#include <LibWeb/HTML/ColorPickerUpdateState.h>
@@ -232,6 +234,15 @@ class WEB_API Page final : public JS::Cell {
232234
void request_clipboard_entries(ClipboardRequest);
233235
void retrieved_clipboard_entries(u64 request_id, Vector<Clipboard::SystemClipboardItem>);
234236

237+
using GeolocationPositionCallback = GC::Ref<GC::Function<void(Optional<Geolocation::CoordinatesData>, Optional<Geolocation::GeolocationPositionError::ErrorCode>)>>;
238+
enum class GeolocationRequestType : u8 {
239+
OneShot,
240+
Watch,
241+
};
242+
u64 request_geolocation_position(GeolocationPositionCallback, GeolocationRequestType = GeolocationRequestType::OneShot);
243+
void cancel_geolocation_position_request(u64 request_id);
244+
void receive_geolocation_position(u64 request_id, Optional<Geolocation::CoordinatesData>, Optional<Geolocation::GeolocationPositionError::ErrorCode>);
245+
235246
enum class PendingNonBlockingDialog {
236247
None,
237248
ColorPicker,
@@ -375,6 +386,14 @@ class WEB_API Page final : public JS::Cell {
375386
HashMap<u64, ClipboardRequest> m_pending_clipboard_requests;
376387
u64 m_next_clipboard_request_id { 0 };
377388

389+
struct PendingGeolocationRequest {
390+
GeolocationPositionCallback callback;
391+
GeolocationRequestType type;
392+
};
393+
HashMap<u64, PendingGeolocationRequest> m_pending_geolocation_requests;
394+
u64 m_next_geolocation_request_id { 0 };
395+
Optional<u64> m_active_geolocation_request_id;
396+
378397
Vector<UniqueNodeID> m_media_elements;
379398
Vector<UniqueNodeID> m_canvas_elements;
380399
Optional<UniqueNodeID> m_media_context_menu_element_id;
@@ -595,6 +614,9 @@ class PageClient : public JS::Cell {
595614
virtual void page_did_request_color_picker([[maybe_unused]] Color current_color) { }
596615
virtual void page_did_request_file_picker([[maybe_unused]] HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles) { }
597616
virtual void page_did_request_select_dropdown([[maybe_unused]] Web::CSSPixelPoint content_position, [[maybe_unused]] Web::CSSPixels minimum_width, [[maybe_unused]] Vector<Web::HTML::SelectItem> items) { }
617+
virtual void page_did_request_geolocation_position([[maybe_unused]] u64 request_id) { }
618+
virtual void page_did_start_geolocation_position_watch([[maybe_unused]] u64 request_id) { }
619+
virtual void page_did_stop_geolocation_position_watch([[maybe_unused]] u64 request_id) { }
598620

599621
virtual void page_did_finish_test([[maybe_unused]] Utf16String const& text) { }
600622
virtual void page_did_set_test_timeout([[maybe_unused]] double milliseconds) { }

0 commit comments

Comments
 (0)