From 524de6bfe9f664fd6bab976cdb0f4ca7fe6e5553 Mon Sep 17 00:00:00 2001 From: Antonio Sartori Date: Tue, 14 Jul 2026 12:21:23 +0200 Subject: [PATCH 1/5] Add support for approximate location --- index.html | 465 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 310 insertions(+), 155 deletions(-) diff --git a/index.html b/index.html index 3c35e4a..14d9159 100644 --- a/index.html +++ b/index.html @@ -77,7 +77,7 @@

returns the device's actual location.

- If an end user [=check permission|grants permission=], + If an end user [=prompt the user to choose|grants permission=], Geolocation:

+

+ This specification provides two ways for users to share their location: +

+
+
+ precise position +
+
+ A position returned by the underlying location information source. By + default, any location data is considered precise, regardless of its + accuracy estimate, unless it has been explicitly coarsened by an + [=approximate location information source=] to be an [=approximate + position=]. +
+
+ approximate position +
+
+ A less [=precise position|precise=] representation of the user's + location. Instead of providing precise coordinates, an [=approximate + location information source=] returns a location that is coarsened + to a larger area, such as the nearest city, postal code, or a + predefined region. What constitutes a coarsened area is left to the + underlying system, which in turn can vary per jurisdiction. +

+ An [=approximate position=] is often sufficient for applications + that don't need to know the user's [=precise position=], for + example, a weather application that only needs a city-level + location. By sharing an [=approximate position=], users get the + benefit of a location-aware application without potentially + revealing their [=precise position=]. +

+
+
+

+ An approximate location information source is a location + information source that returns positions that have been intentionally + obfuscated to make it more difficult to recover the true location. +

Scope @@ -133,8 +172,12 @@

Get current position

- Request the user's current location. If the user allows it, you will - get back a position object. + A website can request a user's location, choosing either a [=precise + position=] or an [=approximate position=] via the + {{PositionOptions/accuracyMode}} member. During the [=prompt the user + to choose|permission request process, the end-user determines the + actual level of accuracy to grant, which the [=user agent=] then uses + to provide the geographic position accordingly.

+

@@ -240,23 +293,34 @@

By default, the API always attempts to return a cached position so - long as it has a previously acquired position. In this example, we - accept a position whose age is no greater than 10 minutes. If the - user agent does not have a fresh enough cached position object, it - automatically acquires a new position. + long as it has a previously acquired position, and that position + matches the requested {{PositionOptions/accuracyMode}}. In this + example, we request a cached position whose age is no greater than 10 + minutes. If the user agent does not have a fresh enough cached + position object of the correct accuracy, it automatically acquires a + new position.

@@ -349,6 +413,17 @@

information also discloses the location of the user of the device, thereby potentially compromising the user's privacy.

+

+ The {{PositionOptions}}'s {{PositionOptions/accuracyMode}} member allows + websites to declare that they have no need for the user's [=precise + position=] and can function with only an [=approximate position=]. When + an application requests an [=approximate position=] an [=approximate + location information source=] is used. Even when a [=precise position=] + is requested, the user agent MAY provide an [=approximate position=] + instead (as defined in the [=acquire a position=] algorithm), for + example, if the user has only granted permission for approximate + accuracy. +

User consent @@ -405,6 +480,16 @@

stored, users need to be allowed to update and delete this information.

+

+ In line with this principle, recipients are strongly encouraged to + request the lowest level of location accuracy that is sufficient for + their application's functionality. For instance, if an application + only needs to know the user's city, it can indicate that to the user + by requesting an [=approximate position=], by setting the + {{PositionOptions/accuracyMode}} option to + {{AccuracyMode/"approximate"}}. This practice of data minimization is + a key aspect of respecting user privacy. +

The recipients of location information need to refrain from retransmitting the location information without the user’s express @@ -464,6 +549,20 @@

the relevant browser tab is closed.

+
+

+ Preventing Precise Location Reconstruction +

+

+ The [=approximate location information source=] used by the user agent + should take into account that a site, by keeping using the API, can + receive multiple [=approximate positions=]. Hence it SHOULD, when + returning an [=approximate position=], implement measures that + mitigate the risk of a refinement attack, preventing the site to + potentially infer a user's [=precise position=] by collecting and + correlating multiple, distinct [=approximate positions=]. +

+

@@ -689,14 +788,17 @@

-
  • Let |descriptor| be a new {{PermissionDescriptor}} whose - {{PermissionDescriptor/name}} is "geolocation". -
  • [=In parallel=]:
      -
    1. Set |permission| to [=request permission to use=] - |descriptor|. +
    2. Let |promptOptions| be the [=set=] « "approximate" » if + |options| {{PositionOptions/accuracyMode}} is + {{AccuracyMode/"approximate"}}, or the [=set=] « "approximate", + "precise" » if |options| {{PositionOptions/accuracyMode}} is + {{AccuracyMode/"precise"}}.
    3. +
    4. Let |permission| be the result of [=prompting the user to + choose=] from |promptOptions| associated with + "geolocation".
    5. If |permission| is "denied", then: @@ -783,9 +885,23 @@

      |timeoutTime|, during which it tries to acquire the device's position by running the following steps:
        -
      1. Let |permission| be [=get the current permission state=] of - "geolocation". -
      2. +
      3. Let |promptOptions| be the [=set=] « "approximate" » if + |options| {{PositionOptions/accuracyMode}} is + {{AccuracyMode/"approximate"}}, or the [=set=] « "approximate", + "precise" » if |options| {{PositionOptions/accuracyMode}} is + {{AccuracyMode/"precise"}}, and let |permission| be the result + of [=prompting the user to choose=] from |promptOptions| + associated with "geolocation". +
      4. If |permission| is "denied":
        1. Stop |timeout|. @@ -795,7 +911,7 @@

      5. -
      6. If |permission| is "granted": +
      7. Otherwise, run the following steps to determine |position|:
        1. Check if an emulated position should be used by running the following steps: @@ -841,7 +957,11 @@

        2. If |cachedPosition|'s {{GeolocationPosition/timestamp}}'s value is greater than - |cacheTime|, and + |cacheTime|, + |cachedPosition|.{{GeolocationPosition/[[accuracyMode]]}} + equals |permission|, and either + |cachedPosition|.{{GeolocationPosition/[[accuracyMode]]}} + is {{AccuracyMode/"approximate"}} or |cachedPosition|.{{GeolocationPosition/[[isHighAccuracy]]}} equals |options|.{{PositionOptions/enableHighAccuracy}}:
            @@ -856,165 +976,170 @@

        3. -
        4. Otherwise, if |position| is not |cachedPosition|, try to - acquire position data from the underlying system, optionally - taking into consideration the value of - |options|.{{PositionOptions/enableHighAccuracy}} during - acquisition. -
        5. -
        6. If the |timeout| elapses during acquisition, or acquiring - the device's position results in failure: +
        7. Otherwise, try to acquire position data from the + underlying system, with the following considerations:
            -
          1. Stop the |timeout|. -
          2. -
          3. Go to dealing with - failures. -
          4. -
          5. Terminate this algorithm. +
          6. If |permission| is {{AccuracyMode/"approximate"}}, + acquire an [=approximate position=]. The + {{PositionOptions/enableHighAccuracy}} member is ignored.
          7. -
          -
        8. -
        9. If acquiring the position data from the - system succeeds: -
            -
          1. Let |positionData| be a [=map=] with the following - name/value pairs based on the acquired position data: -
            -
            - "latitude" -
            -
            - A {{double}} that represents the latitude - coordinates on the Earth's surface in degrees, - using the [[WGS84]] coordinate system. Latitude - measures how far north or south a point is from the - Equator. -
            -
            - "longitude" -
            -
            - A {{double}} that represents the longitude - coordinates on the Earth's surface in degrees, - using the [[WGS84]] coordinate system. Longitude - measures how far east or west a point is from the - Prime Meridian. -
            -
            - "altitude" -
            -
            - A {{double?}} that represents the altitude in - meters above the [[WGS84]] ellipsoid, or `null` if - not available. Altitude measures the height above - sea level. -
            -
            - "accuracy" -
            -
            - A non-negative {{double}} that represents the - accuracy value indicating the 95% confidence level - in meters. Accuracy measures how close the measured - coordinates are to the true position. -
            -
            - "altitudeAccuracy" -
            -
            - A non-negative {{double?}} that represents the - altitude accuracy, or `null` if not available, - indicating the 95% confidence level in meters. - Altitude accuracy measures how close the measured - altitude is to the true altitude. -
            -
            - "speed" -
            -
            - A non-negative {{double?}} that represents the - speed in meters per second, or `null` if not - available. Speed measures how fast the device is - moving. -
            -
            - "heading" -
            -
            - A {{double?}} that represents the heading in - degrees, or `null` if not available or the device - is stationary. Heading measures the direction in - which the device is moving relative to true north. -
            -
            -
          2. -
          3. Set |position| to [=a new `GeolocationPosition`=] - passing |positionData|, |acquisitionTime| and +
          4. If |permission| is {{AccuracyMode/"precise"}}, + acquire a [=precise position=], optionally taking into + consideration the value of |options|.{{PositionOptions/enableHighAccuracy}}.
          5. -
          6. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to - |position|. -
        10. +
        +
      8. +
      9. If the |timeout| elapses during acquisition, or acquiring the + device's position results in failure: +
        1. Stop the |timeout|.
        2. -
        3. [=Queue a task=] on the [=geolocation task source=] with - a step that [=invokes=] |successCallback| with « |position| » - and "`report`". +
        4. [=Deal with failure=]. +
        5. +
        6. Terminate this algorithm.
      10. -
      -
      -
      - Dealing with failures: -
      -
      -
        -
      • If acquiring a position fails, do one of the following - based on the condition that matches the failure: -
        -
        - User or system denied permission: +
      • If acquiring the position data from the + system succeeds: +
          +
        1. Let |positionData| be a [=map=] with the following + name/value pairs based on the acquired position data: +
          +
          + "latitude"
          -

          - [=Call back with error=] passing |errorCallback| and - {{GeolocationPositionError/PERMISSION_DENIED}}. -

          - + A {{double}} that represents the latitude coordinates + on the Earth's surface in degrees, using the [[WGS84]] + coordinate system. Latitude measures how far north or + south a point is from the Equator.
          - Timeout elapsed: + "longitude"
          - [=Call back with error=] with |errorCallback| and - {{GeolocationPositionError/TIMEOUT}}. + A {{double}} that represents the longitude coordinates + on the Earth's surface in degrees, using the [[WGS84]] + coordinate system. Longitude measures how far east or + west a point is from the Prime Meridian.
          - Data acquisition error or any other reason: + "altitude"
          - [=Call back with error=] passing |errorCallback| and - {{GeolocationPositionError/POSITION_UNAVAILABLE}}. + A {{double?}} that represents the altitude in meters + above the [[WGS84]] ellipsoid, or `null` if not + available. Altitude measures the height above sea + level. +
          +
          + "accuracy" +
          +
          + A non-negative {{double}} that represents the accuracy + value indicating the 95% confidence level in meters. + Accuracy measures how close the measured coordinates + are to the true position. +
          +
          + "altitudeAccuracy" +
          +
          + A non-negative {{double?}} that represents the altitude + accuracy, or `null` if not available, indicating the + 95% confidence level in meters. Altitude accuracy + measures how close the measured altitude is to the true + altitude. +
          +
          + "speed" +
          +
          + A non-negative {{double?}} that represents the speed in + meters per second, or `null` if not available. Speed + measures how fast the device is moving. +
          +
          + "heading" +
          +
          + A {{double?}} that represents the heading in degrees, + or `null` if not available or the device is stationary. + Heading measures the direction in which the device is + moving relative to true north.
        2. -
      -
      -
      +
    6. Set |position| to [=a new `GeolocationPosition`=] passing + |positionData|, |acquisitionTime|, + |options|.{{PositionOptions/enableHighAccuracy}}, and + |permission|. +
    7. +
    8. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to + |position|. +
    9. +

    +
  • +
  • Stop the |timeout|. +
  • +
  • [=Queue a task=] on the [=geolocation task source=] with a + step that [=invokes=] |successCallback| with « |position| » and + "`report`". +
  • + +
    +
    + Deal with failure: +
    +
    +
      +
    • If acquiring a position fails, do one of the following based + on the condition that matches the failure: +
      +
      + User or system denied permission: +
      +
      +

      + [=Call back with error=] passing |errorCallback| and + {{GeolocationPositionError/PERMISSION_DENIED}}. +

      + +
      +
      + Timeout elapsed: +
      +
      + [=Call back with error=] with |errorCallback| and + {{GeolocationPositionError/TIMEOUT}}. +
      +
      + Data acquisition error or any other reason: +
      +
      + [=Call back with error=] passing |errorCallback| and + {{GeolocationPositionError/POSITION_UNAVAILABLE}}. +
      +
      +
    • +
    +
    +

    @@ -1043,12 +1168,34 @@

    PositionOptions dictionary

    +        enum AccuracyMode {
    +          "precise",
    +          "approximate"
    +        };
    +
             dictionary PositionOptions {
    +          AccuracyMode accuracyMode = "precise";
               boolean enableHighAccuracy = false;
               [Clamp] unsigned long timeout = 0xFFFFFFFF;
               [Clamp] unsigned long maximumAge = 0;
             };
             
    +
    +

    + `accuracyMode` member +

    +

    + The accuracyMode member is used to request a specific + level of accuracy. +

    + +

    `enableHighAccuracy` member @@ -1168,6 +1315,14 @@

    {{PositionOptions/enableHighAccuracy}} member when this {{GeolocationPosition}} is [=a new GeolocationPosition|created=]. + + [[\accuracyMode]] + + + An {{AccuracyMode}} that records the chosen {{AccuracyMode}} + when this {{GeolocationPosition}} is [=a new + GeolocationPosition|created=]. +

    From 2abd0f1cfa574c6814775f54bf1b7b32cd00fc91 Mon Sep 17 00:00:00 2001 From: Antonio Sartori Date: Mon, 27 Jul 2026 08:15:07 +0200 Subject: [PATCH 2/5] Implement copilot + Marco's requested changes --- index.html | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 14d9159..0330f75 100644 --- a/index.html +++ b/index.html @@ -417,7 +417,7 @@

    The {{PositionOptions}}'s {{PositionOptions/accuracyMode}} member allows websites to declare that they have no need for the user's [=precise position=] and can function with only an [=approximate position=]. When - an application requests an [=approximate position=] an [=approximate + an application requests an [=approximate position=], an [=approximate location information source=] is used. Even when a [=precise position=] is requested, the user agent MAY provide an [=approximate position=] instead (as defined in the [=acquire a position=] algorithm), for @@ -555,13 +555,20 @@

    The [=approximate location information source=] used by the user agent - should take into account that a site, by keeping using the API, can - receive multiple [=approximate positions=]. Hence it SHOULD, when + should take into account that a site, by repeatedly using the API, can + receive multiple [=approximate positions=]. Therefore it SHOULD, when returning an [=approximate position=], implement measures that - mitigate the risk of a refinement attack, preventing the site to - potentially infer a user's [=precise position=] by collecting and + mitigate the risk of a refinement attack, preventing the site from + potentially inferring a user's [=precise position=] by collecting and correlating multiple, distinct [=approximate positions=].

    +

    + The chosen accuracy is per request. A user agent that internally + shares one acquired position across concurrent requests coarsens it + for each {{AccuracyMode/"approximate"}} request when the position is + delivered, so an approximate request never receives a precise + position acquired for a concurrent request. +

    @@ -799,6 +806,7 @@

  • Let |permission| be the result of [=prompting the user to choose=] from |promptOptions| associated with "geolocation". +
  • If |permission| is "denied", then: @@ -902,6 +910,7 @@

    position=].

    +

  • If |permission| is "denied":
    1. Stop |timeout|. @@ -934,7 +943,8 @@

    2. Let |position| be [=a new `GeolocationPosition`=] passing |emulatedPositionData|, |acquisitionTime| and - |options|.{{PositionOptions/enableHighAccuracy}}. + |options|.{{PositionOptions/enableHighAccuracy}}, and + |permission|.
    3. [=Queue a task=] on the [=geolocation task source=] with a step that [=invokes=] @@ -1195,6 +1205,16 @@

      {{PositionOptions/enableHighAccuracy}} member.

      +

  • @@ -1315,6 +1335,8 @@

    {{PositionOptions/enableHighAccuracy}} member when this {{GeolocationPosition}} is [=a new GeolocationPosition|created=]. + + [[\accuracyMode]] @@ -1424,8 +1446,9 @@

    A new `GeolocationPosition` is constructed with [=map=] - |positionData|, {{EpochTimeStamp}} |timestamp:EpochTimeStamp| and - boolean |isHighAccuracy| by performing the following steps: + |positionData|, {{EpochTimeStamp}} |timestamp:EpochTimeStamp|, + boolean |isHighAccuracy|, and {{AccuracyMode}} |accuracyMode| by + performing the following steps:

    1. Let |coords:GeolocationCoordinates| be a newly created @@ -1440,8 +1463,10 @@

    2. Return a newly created {{GeolocationPosition}} instance with its {{GeolocationPosition/coords}} attribute initialized to |coords| and {{GeolocationPosition/timestamp}} attribute initialized to - |timestamp|, and its {{GeolocationPosition/[[isHighAccuracy]]}} - internal slot set to |isHighAccuracy|. + |timestamp|, its {{GeolocationPosition/[[isHighAccuracy]]}} + internal slot set to |isHighAccuracy|, and its + {{GeolocationPosition/[[accuracyMode]]}} internal slot set to + |accuracyMode|.
    From e0167428e39045ef594f86c80bb0cb4b0e4c375f Mon Sep 17 00:00:00 2001 From: Antonio Sartori Date: Mon, 27 Jul 2026 08:28:58 +0200 Subject: [PATCH 3/5] Additional bug fixes --- index.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 0330f75..a508f2c 100644 --- a/index.html +++ b/index.html @@ -175,7 +175,7 @@

    A website can request a user's location, choosing either a [=precise position=] or an [=approximate position=] via the {{PositionOptions/accuracyMode}} member. During the [=prompt the user - to choose|permission request process, the end-user determines the + to choose|permission request process=], the end-user determines the actual level of accuracy to grant, which the [=user agent=] then uses to provide the geographic position accordingly.

    @@ -918,6 +918,8 @@

  • Do the user or system denied permission failure case step.
  • +
  • Terminate this algorithm. +
  • Otherwise, run the following steps to determine |position|: @@ -986,8 +988,8 @@

  • -
  • Otherwise, try to acquire position data from the - underlying system, with the following considerations: +
  • Ty to acquire position data from the underlying system, + with the following considerations:
    1. If |permission| is {{AccuracyMode/"approximate"}}, acquire an [=approximate position=]. The From ed24cddd87e09dd89e70fa67e64e2322dd7783dc Mon Sep 17 00:00:00 2001 From: Antonio Sartori Date: Wed, 29 Jul 2026 07:29:01 +0200 Subject: [PATCH 4/5] fix typo --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index a508f2c..ec3065a 100644 --- a/index.html +++ b/index.html @@ -988,7 +988,7 @@

  • -
  • Ty to acquire position data from the underlying system, +
  • Try to acquire position data from the underlying system, with the following considerations:
    1. If |permission| is {{AccuracyMode/"approximate"}}, From 1bd58dae1b4c7804ee560a7356974ea792f5ddb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20C=C3=A1ceres?= Date: Thu, 30 Jul 2026 14:40:52 +1000 Subject: [PATCH 5/5] Editorial: name the chosen accuracy |choice|, not |permission| (#2) * Editorial: name the chosen accuracy |choice|, not |permission| * Null altitude, altitudeAccuracy, speed, and heading for approximate positions --- index.html | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index ec3065a..45c88cb 100644 --- a/index.html +++ b/index.html @@ -803,13 +803,13 @@

      "precise" » if |options| {{PositionOptions/accuracyMode}} is {{AccuracyMode/"precise"}}.

    2. -
    3. Let |permission| be the result of [=prompting the user to +
    4. Let |choice| be the result of [=prompting the user to choose=] from |promptOptions| associated with "geolocation".
    5. - If |permission| is "denied", then: + If |choice| is "denied", then:
      1. If |watchId| was passed, [=list/remove=] |watchId| from |watchIDs|. @@ -897,7 +897,7 @@

        |options| {{PositionOptions/accuracyMode}} is {{AccuracyMode/"approximate"}}, or the [=set=] « "approximate", "precise" » if |options| {{PositionOptions/accuracyMode}} is - {{AccuracyMode/"precise"}}, and let |permission| be the result + {{AccuracyMode/"precise"}}, and let |choice| be the result of [=prompting the user to choose=] from |promptOptions| associated with "geolocation".

      2. -
      3. If |permission| is "denied": +
      4. If |choice| is "denied":
        1. Stop |timeout|.
        2. @@ -944,9 +944,9 @@

      5. Let |position| be [=a new `GeolocationPosition`=] - passing |emulatedPositionData|, |acquisitionTime| and + passing |emulatedPositionData|, |acquisitionTime|, |options|.{{PositionOptions/enableHighAccuracy}}, and - |permission|. + |choice|.
      6. [=Queue a task=] on the [=geolocation task source=] with a step that [=invokes=] @@ -971,7 +971,7 @@

        {{GeolocationPosition/timestamp}}'s value is greater than |cacheTime|, |cachedPosition|.{{GeolocationPosition/[[accuracyMode]]}} - equals |permission|, and either + equals |choice|, and either |cachedPosition|.{{GeolocationPosition/[[accuracyMode]]}} is {{AccuracyMode/"approximate"}} or |cachedPosition|.{{GeolocationPosition/[[isHighAccuracy]]}} @@ -991,11 +991,11 @@

      7. Try to acquire position data from the underlying system, with the following considerations:
          -
        1. If |permission| is {{AccuracyMode/"approximate"}}, +
        2. If |choice| is {{AccuracyMode/"approximate"}}, acquire an [=approximate position=]. The {{PositionOptions/enableHighAccuracy}} member is ignored.
        3. -
        4. If |permission| is {{AccuracyMode/"precise"}}, +
        5. If |choice| is {{AccuracyMode/"precise"}}, acquire a [=precise position=], optionally taking into consideration the value of |options|.{{PositionOptions/enableHighAccuracy}}. @@ -1089,7 +1089,7 @@

        6. Set |position| to [=a new `GeolocationPosition`=] passing |positionData|, |acquisitionTime|, |options|.{{PositionOptions/enableHighAccuracy}}, and - |permission|. + |choice|.
        7. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to |position|. @@ -1453,6 +1453,10 @@

          performing the following steps:

            +
          1. If |accuracyMode| is {{AccuracyMode/"approximate"}}, set + |positionData|["altitude"], |positionData|["altitudeAccuracy"], + |positionData|["speed"], and |positionData|["heading"] to null. +
          2. Let |coords:GeolocationCoordinates| be a newly created {{GeolocationCoordinates}} instance.