From 9de8608a20efc8b27f20b6855e6425cb968cf6ce Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Thu, 4 Sep 2025 18:33:41 -0700 Subject: [PATCH 1/9] Add support for approximate geolocation This commit introduces the ability for users and developers to request a less precise, privacy-preserving "approximate" location. Key changes include: - A new `accuracyMode` option in `PositionOptions` to request either "precise" or "approximate" location. - A new `geolocation-approximate` permission and policy, which is implicitly granted if the "geolocation" permission is given. - Separate internal caching for precise and approximate positions. - Updated "Request a position" and "Acquire a position" algorithms to handle the new accuracy levels, permission fallback, and caching logic. - The `GeolocationPosition` interface now includes an `accuracyMode` attribute to reflect the accuracy of the returned position. - Expanded privacy and user consent sections to discuss the benefits and user interface implications of approximate location. --- index.html | 634 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 396 insertions(+), 238 deletions(-) diff --git a/index.html b/index.html index 40ca33b..fcd8558 100644 --- a/index.html +++ b/index.html @@ -91,6 +91,15 @@

location information sources, and no guarantee is given that the API returns the device's actual location.

+

+ To better protect user privacy, this specification also provides a way + for users to share an approximate location instead of a + precise one. This is often sufficient for applications that don't need + to know the user's exact location, for example, a weather application + just needs a city-level location. By sharing an approximate location, + users can still get the benefit of a location-aware application without + revealing their exact whereabouts. +

If an end user [=check permission|grants permission=], Geolocation: @@ -149,7 +158,11 @@

Request the user's current location. If the user allows it, you will - get back a position object. + get back a position object. The website can influence the position's + accuracy by using the `accuracyMode` option. By default, the user + agent will provide a `"precise"` location, but a website can request + an `"approximate"` location if it doesn't require high accuracy, + which helps protect user privacy.

+

@@ -255,17 +278,18 @@

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 that matches the + requested `accuracyMode`. In this example, we accept a precise + 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.

-

@@ -329,14 +369,23 @@

Third-party usage can be selectively enabled by adding the - [^iframe/allow^]`="geolocation"` attribute to an [^iframe^] element: + [^iframe/allow^]`="geolocation"` or `allow="geolocation-approximate"` + attribute to an [^iframe^] element. Note that if `geolocation` is + allowed, `geolocation-approximate` is also implicitly allowed.

@@ -345,7 +394,7 @@

@@ -364,6 +413,16 @@

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

+

+ To mitigate this privacy risk, this specification introduces the + {{PositionOptions/accuracyMode}} option, which allows users to share an + approximate location. This allows applications to function without + needing the user's precise whereabouts, thus offering a more + privacy-friendly alternative. When an application requests an + approximate location, the user agent can provide a less precise + location that still meets the application's needs while protecting the + user's exact location. +

User consent @@ -378,7 +437,14 @@

An end-user will generally give [=express permission=] through a user - interface, which usually present a range of permission + interface. With the introduction of approximate location, this + interface SHOULD allow the user to choose between sharing a precise + or an approximate location. This choice gives users more control over + their privacy, allowing them to share a less precise location when + they are not comfortable sharing their exact whereabouts. +

+

+ The user interface will usually present a range of permission [=permission/lifetimes=] that the end-user can choose from. The choice of [=permission/lifetimes=] vary across user agents, but they are typically time-based (e.g., "a day"), or until browser is closed, @@ -420,6 +486,15 @@

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 should request an approximate + location using the `accuracyMode` option rather than a precise one. + 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,19 +539,30 @@

Geolocation is a [=default powerful feature=] identified - by the [=powerful feature/name=] "geolocation". + by two permission names: "geolocation" for precise location and + "geolocation-approximate" + for approximate location. +

+

+ When a website requests location access, the user agent [=check + permission|checks permission=] for the level of accuracy specified by + the {{PositionOptions/accuracyMode}} option. A grant for the + "geolocation" permission implies a grant for the + "geolocation-approximate" permission.

When checking permission - to use the API, a user agent MAY suggest time-based [=permission=] - [=permission/lifetimes=], such as "24 hours", "1 week", or choose to - remember the permission [=permission/grant=] indefinitely. However, - it is RECOMMENDED that a user agent prioritize restricting the - [=permission=] [=permission/lifetime=] to a single session: This can - be, for example, until the [=environment settings object/realm=] is - destroyed, the end-user [=navigates=] away from the [=origin=], or - the relevant browser tab is closed. + to use the API, a user agent SHOULD present the user with a choice to + grant access to their precise location, their approximate location, + or to deny the request. A user agent MAY also suggest time-based + [=permission=] [=permission/lifetimes=], such as "24 hours", "1 + week", or choose to remember the permission [=permission/grant=] + indefinitely. However, it is RECOMMENDED that a user agent prioritize + restricting the [=permission=] [=permission/lifetime=] to a single + session: This can be, for example, until the [=environment settings + object/realm=] is destroyed, the end-user [=navigates=] away from the + [=origin=], or the relevant browser tab is closed.

@@ -549,12 +635,25 @@

- [[\cachedPosition]] + [[\cachedPrecisePosition]] A {{GeolocationPosition}}, initialized to null. It's a reference - to the last acquired position and serves as a cache. A user agent - MAY evict {{Geolocation/[[cachedPosition]]}} by resetting it to + to the last acquired precise position and serves as a cache. A + user agent MAY evict {{Geolocation/[[cachedPrecisePosition]]}} by + resetting it to null at any time for any reason. + + + + + [[\cachedApproximatePosition]] + + + A {{GeolocationPosition}}, initialized to null. It's a reference + to the last acquired approximate position and serves as a cache. + A user agent MAY evict + {{Geolocation/[[cachedApproximatePosition]]}} by resetting it to null at any time for any reason. @@ -676,8 +775,12 @@

  • Let |document:Document| be the |geolocation|'s [=relevant global object=]'s [=associated `Document`=].
  • +
  • Let |permissionName| be "geolocation-approximate" if + |options|.{{PositionOptions/accuracyMode}} is "approximate", + otherwise let it be "geolocation". +
  • If |document| is not [=allowed to use=] the - "geolocation" feature: + |permissionName| feature:
    1. If |watchId| was passed, [=List/remove=] |watchId| from |watchIDs|. @@ -718,7 +821,7 @@

  • Let |descriptor| be a new {{PermissionDescriptor}} whose - {{PermissionDescriptor/name}} is "geolocation". + {{PermissionDescriptor/name}} is |permissionName|.
  • [=In parallel=]:
      @@ -804,262 +907,271 @@

    1. Let |timeoutTime| be the sum of |acquisitionTime| and |options|.{{PositionOptions/timeout}}.
    2. -
    3. Let |cachedPosition:GeolocationPosition| be [=this=]'s - {{Geolocation/[[cachedPosition]]}}. -
    4. Create an implementation-specific |timeout| task that elapses at |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. Let |precisePermission| be [=get the current permission + state=] of "geolocation".
      3. -
      4. If |permission| is "denied": +
      5. Let |approximatePermission| be [=get the current permission + state=] of "geolocation-approximate". +
      6. +
      7. Let |effectiveAccuracy| be null. +
      8. +
      9. If |options|.{{PositionOptions/accuracyMode}} is + "approximate": +
          +
        1. If |approximatePermission| is "granted", set + |effectiveAccuracy| to "approximate". +
        2. +
        +
      10. +
      11. Else if |options|.{{PositionOptions/accuracyMode}} is + "precise": +
          +
        1. If |precisePermission| is "granted", set + |effectiveAccuracy| to "precise". +
        2. +
        3. Else if |approximatePermission| is "granted", set + |effectiveAccuracy| to "approximate". +
        4. +
        +
      12. +
      13. If |effectiveAccuracy| is null:
        1. Stop |timeout|.
        2. Do the user or system denied permission failure case step.
        3. +
        4. Terminate this algorithm. +
      14. -
      15. If |permission| is "granted": +
      16. + Check if an emulated position should be used by + running the following steps:
          -
        1. - Check if an emulated position should be - used by running the following steps: +
        2. Let |emulatedPositionData| be [=get emulated position + data=] passing [=this=]. +
        3. +
        4. If |emulatedPositionData| is not null:
            -
          1. Let |emulatedPositionData| be [=get emulated position - data=] passing [=this=]. -
          2. -
          3. If |emulatedPositionData| is not null: +
          4. If |emulatedPositionData| is a + {{GeolocationPositionError}}:
              -
            1. If |emulatedPositionData| is a - {{GeolocationPositionError}}: -
                -
              1. [=Call back with error=] passing - |errorCallback| and - |emulatedPositionData|.{{GeolocationPositionError/code}}. -
              2. -
              3. Terminate this algorithm. -
              4. -
              -
            2. -
            3. Let |position| be [=a new `GeolocationPosition`=] - passing |emulatedPositionData|, |acquisitionTime| and - |options|.{{PositionOptions/enableHighAccuracy}}. -
            4. -
            5. [=Queue a task=] on the [=geolocation task - source=] with a step that [=invokes=] - |successCallback| with « |position| » and "`report`". +
            6. [=Call back with error=] passing |errorCallback| + and + |emulatedPositionData|.{{GeolocationPositionError/code}}.
            7. Terminate this algorithm.
          5. -
          -
        5. -
        6. Let |position| be null. -
        7. -
        8. If |cachedPosition| is not null, and - |options|.{{PositionOptions/maximumAge}} is greater than 0: -
            -
          1. Let |cacheTime:long| be |acquisitionTime| minus the - value of the |options|.{{PositionOptions/maximumAge}} - member. +
          2. Let |position| be [=a new `GeolocationPosition`=] + passing |emulatedPositionData|, |acquisitionTime|, + |options|.{{PositionOptions/enableHighAccuracy}}, and + |effectiveAccuracy|.
          3. -
          4. - If |cachedPosition|'s - {{GeolocationPosition/timestamp}}'s value is greater - than |cacheTime|, and - |cachedPosition|.{{GeolocationPosition/[[isHighAccuracy]]}} - equals - |options|.{{PositionOptions/enableHighAccuracy}}, - set |position| to |cachedPosition|. : -
              -
            1. [=Queue a task=] on the [=geolocation task - source=] with a step that [=invokes=] - |successCallback| with « |cachedPosition| » and - "`report`". -
            2. -
            3. Terminate this algorithm. -
            4. -
            +
          5. [=Queue a task=] on the [=geolocation task source=] + with a step that [=invokes=] |successCallback| with « + |position| » and "`report`". +
          6. +
          7. Terminate this algorithm.
        9. -
        10. 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. +
        +
      17. +
      18. Let |cachedPosition| be null. +
      19. +
      20. If |effectiveAccuracy| is "precise": +
          +
        1. Set |cachedPosition| to [=this=]'s + {{Geolocation/[[cachedPrecisePosition]]}}.
        2. -
        3. If the |timeout| elapses during acquisition, or acquiring - the device's position results in failure: +
        +
      21. +
      22. Else if |effectiveAccuracy| is "approximate": +
          +
        1. Set |cachedPosition| to [=this=]'s + {{Geolocation/[[cachedApproximatePosition]]}}. +
        2. +
        +
      23. +
      24. Let |position| be null. +
      25. +
      26. If |cachedPosition| is not null, and + |options|.{{PositionOptions/maximumAge}} is greater than 0: +
          +
        1. Let |cacheTime:long| be |acquisitionTime| minus the value + of the |options|.{{PositionOptions/maximumAge}} member. +
        2. +
        3. + If |cachedPosition|'s + {{GeolocationPosition/timestamp}}'s value is greater than + |cacheTime|, and (|effectiveAccuracy| is "approximate" or + (|effectiveAccuracy| is "precise" and + |cachedPosition|.{{GeolocationPosition/[[isHighAccuracy]]}} + equals + |options|.{{PositionOptions/enableHighAccuracy}})), + set |position| to |cachedPosition|. :
            -
          1. Stop the |timeout|. -
          2. -
          3. Go to dealing with - failures. +
          4. [=Queue a task=] on the [=geolocation task source=] + with a step that [=invokes=] |successCallback| with « + |cachedPosition| » and "`report`".
          5. Terminate this algorithm.
          6. -
          +
      27. -
      28. 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: -
          -
          - "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 - |options|.{{PositionOptions/enableHighAccuracy}}. -
        4. -
        5. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to - |position|. -
        6. -
        -
          -
        1. Set |position| to [=a new `GeolocationPosition`=] - passing |acquisitionTime| and - |options|.{{PositionOptions/enableHighAccuracy}}. -
        2. -
        3. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to - |position|. -
        4. -
        +
      +
    5. +
    6. Otherwise, if |position| is not |cachedPosition|, try to + acquire position data from the underlying system, with the + following considerations: +
        +
      1. If |effectiveAccuracy| is "approximate", the user agent + SHOULD acquire a position with a level of accuracy that is + sufficient to protect user privacy (e.g., city-level + accuracy). The {{PositionOptions/enableHighAccuracy}} member + is ignored.
      2. +
      3. If |effectiveAccuracy| is "precise", the user agent + SHOULD acquire a position, optionally taking into + consideration the value of + |options|.{{PositionOptions/enableHighAccuracy}}. +
      4. +
      +
    7. +
    8. 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. Go to dealing with + failures. +
      5. +
      6. Terminate this algorithm.
    9. -

    -
    -
    - 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: +
        +
        + "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"
        -

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

        - + A {{double?}} that represents the altitude in meters + above the [[WGS84]] ellipsoid, or `null` if not + available. Altitude measures the height above sea + level.
        - Timeout elapsed: + "accuracy"
        - [=Call back with error=] with |errorCallback| and - {{GeolocationPositionError/TIMEOUT}}. + 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.
        - Data acquisition error or any other reason: + "altitudeAccuracy"
        - [=Call back with error=] passing |errorCallback| and - {{GeolocationPositionError/POSITION_UNAVAILABLE}}. + 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. -
    -
    -
    +
  • Set |position| to [=a new `GeolocationPosition`=] passing + |positionData|, |acquisitionTime|, + |options|.{{PositionOptions/enableHighAccuracy}}, and + |effectiveAccuracy|. +
  • +
  • If |effectiveAccuracy| is "precise": +
      +
    1. Set [=this=]'s + {{Geolocation/[[cachedPrecisePosition]]}} to |position|. +
    2. +
    +
  • +
  • Else if |effectiveAccuracy| is "approximate": +
      +
    1. Set [=this=]'s + {{Geolocation/[[cachedApproximatePosition]]}} to + |position|. +
    2. +
    +
  • + +
      +
    1. Set |position| to [=a new `GeolocationPosition`=] passing + |acquisitionTime| and + |options|.{{PositionOptions/enableHighAccuracy}}. +
    2. +
    3. Set [=this=]'s {{Geolocation/[[cachedPosition]]}} to + |position|. +
    4. +
    + +
  • Stop the |timeout|. +
  • +
  • [=Queue a task=] on the [=geolocation task source=] with a + step that [=invokes=] |successCallback| with « |position| » and + "`report`". +
  • + @@ -1090,12 +1202,36 @@

    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. It can be set to "precise" to request the most + accurate location data available, or "approximate" to receive a less + accurate location that protects user privacy. The default value is + "precise". +

    + +

    `enableHighAccuracy` member @@ -1160,6 +1296,7 @@

    interface GeolocationPosition { readonly attribute GeolocationCoordinates coords; readonly attribute EpochTimeStamp timestamp; + readonly attribute AccuracyMode accuracyMode; [Default] object toJSON(); }; @@ -1180,6 +1317,17 @@

    geographic position of the device was acquired.

    +
    +

    + `accuracyMode` attribute +

    +

    + The accuracyMode attribute indicates the accuracy level of + the position, which can be either `"precise"` or `"approximate"`. + This reflects the level of accuracy that was granted and used to + obtain the position. +

    +

    `toJSON()` method @@ -1346,8 +1494,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 string |effectiveAccuracy| by + performing the following steps:

    1. Let |coords:GeolocationCoordinates| be a newly created @@ -1360,10 +1509,12 @@

  • Return a newly created {{GeolocationPosition}} instance with its - {{GeolocationPosition/coords}} attribute initialized to |coords| and + {{GeolocationPosition/coords}} attribute initialized to |coords|, {{GeolocationPosition/timestamp}} attribute initialized to - |timestamp|, and its {{GeolocationPosition/[[isHighAccuracy]]}} - internal slot set to |isHighAccuracy|. + |timestamp|, {{GeolocationPosition/accuracyMode}} attribute + initialized to |effectiveAccuracy|, and its + {{GeolocationPosition/[[isHighAccuracy]]}} internal slot set to + |isHighAccuracy|.
  • @@ -1504,10 +1655,17 @@

    Permissions policy

    - This specification defines a [=policy-controlled feature=] identified - by the token string "geolocation". Its [=policy-controlled + This specification defines two [=policy-controlled features=] + identified by the token strings "geolocation" and + "geolocation-approximate". Their [=policy-controlled feature/default allowlist=] is [=default allowlist/'self'=].

    +

    + The "geolocation" policy controls access to precise location, while + "geolocation-approximate" controls access to approximate location. + Granting "geolocation" permission implicitly grants + "geolocation-approximate" permission as well. +

    From 78c9dacf5cd5d3b7104a3d8a36a799a75ff48331 Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Thu, 4 Sep 2025 18:58:30 -0700 Subject: [PATCH 2/9] fixup! Add support for approximate geolocation --- index.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/index.html b/index.html index fcd8558..dbd6c2f 100644 --- a/index.html +++ b/index.html @@ -657,6 +657,18 @@

    null at any time for any reason. + + + [[\cachedPosition]] + + + A {{GeolocationPosition}}, initialized to null. It's a + reference to the last acquired position and serves as a cache. A + user agent MAY evict {{Geolocation/[[cachedPosition]]}} by + resetting it to null at any time for any reason. + + [[\watchIDs]] From 24484bc97ea1639bc31c378081d96fb3d4811338 Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Fri, 5 Sep 2025 11:13:57 -0700 Subject: [PATCH 3/9] Docs: Add description and privacy considerations for approximate location --- index.html | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index dbd6c2f..65dbb10 100644 --- a/index.html +++ b/index.html @@ -100,6 +100,9 @@

    users can still get the benefit of a location-aware application without revealing their exact whereabouts.

    +

    + An approximate location is a less precise, privacy-preserving representation of the user's location. Instead of providing exact coordinates, the user agent might return a location that is intentionally coarsened to a larger area, such as the nearest city, postal code, or a predefined region. This allows users to receive locally relevant information without disclosing their precise position, giving them more control over their privacy. +

    If an end user [=check permission|grants permission=], Geolocation: @@ -565,6 +568,14 @@

    [=origin=], or the relevant browser tab is closed.

    +
    +

    + Preventing Precise Location Reconstruction +

    +

    + A malicious actor could potentially infer a user's precise location by collecting and correlating multiple, distinct approximate locations. To mitigate this risk of a refinement attack, when a site receives an approximate location, any subsequent calls from that same site within a user-agent-defined time window SHOULD return the exact same, cached approximate location data. A user agent might, for example, use a time window of 15 minutes. +

    +

    @@ -840,17 +851,24 @@

  • Set |permission| to [=request permission to use=] |descriptor|.
  • +
  • + Let |handle permission denial| be the following steps: +
      +
    1. If |watchId| was passed, [=list/remove=] |watchId| from |watchIDs|.
    2. +
    3. [=Call back with error=] passing |errorCallback| and {{GeolocationPositionError/PERMISSION_DENIED}}.
    4. +
    5. Terminate this algorithm.
    6. +
    +
  • - If |permission| is "denied", then: + If |permission| is "denied":
      -
    1. If |watchId| was passed, [=list/remove=] |watchId| from - |watchIDs|. -
    2. -
    3. [=Call back with error=] passing |errorCallback| and - {{GeolocationPositionError/PERMISSION_DENIED}}. -
    4. -
    5. Terminate this algorithm. +
    6. If |options|.{{PositionOptions/accuracyMode}} is "approximate", then run |handle permission denial|.
    7. +
    8. Else: +
        +
      1. Let |approximatePermission| be [=get the current permission state=] of "geolocation-approximate".
      2. +
      3. If |approximatePermission| is "denied", then run |handle permission denial|.
      4. +
  • From b2d9d850c37c54e3315c5b9a32d35faf99709a2b Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Fri, 5 Sep 2025 11:45:47 -0700 Subject: [PATCH 4/9] chore: Tidy HTML --- index.html | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 65dbb10..d225430 100644 --- a/index.html +++ b/index.html @@ -101,7 +101,13 @@

    revealing their exact whereabouts.

    - An approximate location is a less precise, privacy-preserving representation of the user's location. Instead of providing exact coordinates, the user agent might return a location that is intentionally coarsened to a larger area, such as the nearest city, postal code, or a predefined region. This allows users to receive locally relevant information without disclosing their precise position, giving them more control over their privacy. + An approximate location is a less precise, privacy-preserving + representation of the user's location. Instead of providing exact + coordinates, the user agent might return a location that is + intentionally coarsened to a larger area, such as the nearest city, + postal code, or a predefined region. This allows users to receive + locally relevant information without disclosing their precise position, + giving them more control over their privacy.

    If an end user [=check permission|grants permission=], @@ -573,7 +579,13 @@

    Preventing Precise Location Reconstruction

    - A malicious actor could potentially infer a user's precise location by collecting and correlating multiple, distinct approximate locations. To mitigate this risk of a refinement attack, when a site receives an approximate location, any subsequent calls from that same site within a user-agent-defined time window SHOULD return the exact same, cached approximate location data. A user agent might, for example, use a time window of 15 minutes. + A malicious actor could potentially infer a user's precise location + by collecting and correlating multiple, distinct approximate + locations. To mitigate this risk of a refinement attack, when a site + receives an approximate location, any subsequent calls from that same + site within a user-agent-defined time window SHOULD return the exact + same, cached approximate location data. A user agent might, for + example, use a time window of 15 minutes.

    @@ -851,23 +863,33 @@

  • Set |permission| to [=request permission to use=] |descriptor|.
  • -
  • - Let |handle permission denial| be the following steps: +
  • Let |handle permission denial| be the following steps:
      -
    1. If |watchId| was passed, [=list/remove=] |watchId| from |watchIDs|.
    2. -
    3. [=Call back with error=] passing |errorCallback| and {{GeolocationPositionError/PERMISSION_DENIED}}.
    4. -
    5. Terminate this algorithm.
    6. +
    7. If |watchId| was passed, [=list/remove=] |watchId| from + |watchIDs|. +
    8. +
    9. [=Call back with error=] passing |errorCallback| and + {{GeolocationPositionError/PERMISSION_DENIED}}. +
    10. +
    11. Terminate this algorithm. +
  • If |permission| is "denied":
      -
    1. If |options|.{{PositionOptions/accuracyMode}} is "approximate", then run |handle permission denial|.
    2. +
    3. If |options|.{{PositionOptions/accuracyMode}} is + "approximate", then run |handle permission denial|. +
    4. Else:
        -
      1. Let |approximatePermission| be [=get the current permission state=] of "geolocation-approximate".
      2. -
      3. If |approximatePermission| is "denied", then run |handle permission denial|.
      4. +
      5. Let |approximatePermission| be [=get the current + permission state=] of "geolocation-approximate". +
      6. +
      7. If |approximatePermission| is "denied", then run + |handle permission denial|. +
    From 07711d6ff8cb780ec9e3bba6db96b6c65def878f Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Mon, 8 Sep 2025 21:52:18 -0700 Subject: [PATCH 5/9] fix: Address all outstanding review comments --- index.html | 133 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/index.html b/index.html index d225430..bd7aebf 100644 --- a/index.html +++ b/index.html @@ -88,26 +88,38 @@

    System (GPS) and location inferred from network signals such as IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs, as well as user input. The API itself is agnostic of the underlying - location information sources, and no guarantee is given that the API - returns the device's actual location. + location information sources, and no guarantee is given that + the API returns the device's actual location.

    - To better protect user privacy, this specification also provides a way - for users to share an approximate location instead of a - precise one. This is often sufficient for applications that don't need - to know the user's exact location, for example, a weather application - just needs a city-level location. By sharing an approximate location, - users can still get the benefit of a location-aware application without - revealing their exact whereabouts. + This specification provides two ways for users to share their location: + a precise position and an approximate position.

    - An approximate location is a less precise, privacy-preserving - representation of the user's location. Instead of providing exact - coordinates, the user agent might return a location that is - intentionally coarsened to a larger area, such as the nearest city, - postal code, or a predefined region. This allows users to receive - locally relevant information without disclosing their precise position, - giving them more control over their privacy. + A precise position is 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=]. +

    +

    + An approximate position is a less precise, + privacy-preserving representation of the user's location. Instead of + providing precise coordinates, the user agent might return a location + that is intentionally coarsened to a larger area, such as the nearest + city, postal code, or a predefined region. This 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 can still get + the benefit of a location-aware application without revealing their + [=precise position=]. +

    +

    + An approximate location information source is a [=location + information source=] that provides an [=approximate position=]. For + example, the user agent itself can be an [=approximate location + information source=] by taking a [=precise position=] and coarsening + it.

    If an end user [=check permission|grants permission=], @@ -424,13 +436,13 @@

    To mitigate this privacy risk, this specification introduces the - {{PositionOptions/accuracyMode}} option, which allows users to share an - approximate location. This allows applications to function without - needing the user's precise whereabouts, thus offering a more - privacy-friendly alternative. When an application requests an - approximate location, the user agent can provide a less precise + {{PositionOptions/accuracyMode}} option, which allows the application + to request an [=approximate position=]. This allows applications to + function without needing the user's [=precise position=], thus offering + a more privacy-friendly alternative. When an application requests an + [=approximate position=], the user agent can provide a less precise location that still meets the application's needs while protecting the - user's exact location. + user's [=precise position=].

    @@ -446,11 +458,12 @@

    An end-user will generally give [=express permission=] through a user - interface. With the introduction of approximate location, this - interface SHOULD allow the user to choose between sharing a precise - or an approximate location. This choice gives users more control over + interface. With the introduction of [=approximate position=], [=User + agents=] with support for [=approximate position=] SHOULD allow the + user to choose between sharing a [=precise position=] or an + [=approximate position=]. This choice gives users more control over their privacy, allowing them to share a less precise location when - they are not comfortable sharing their exact whereabouts. + they are not comfortable sharing their [=precise position=].

    The user interface will usually present a range of permission @@ -499,10 +512,12 @@

    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 should request an approximate - location using the `accuracyMode` option rather than a precise one. - This practice of data minimization is a key aspect of respecting user - privacy. + only needs to know the user's city, it should request an + [=approximate position=] by setting the `accuracyMode` option to + `"approximate"`. This is preferable to requesting a [=precise + position=], which is done by either setting `accuracyMode` to + `"precise"` or by leaving the option unset. This practice of data + minimization is a key aspect of respecting user privacy.

    The recipients of location information need to refrain from @@ -548,10 +563,10 @@

    Geolocation is a [=default powerful feature=] identified - by two permission names: "geolocation" for precise location and + by the [=powerful feature/name=]s "geolocation" for [=precise position=] and "geolocation-approximate" - for approximate location. + for [=approximate position=].

    When a website requests location access, the user agent [=check @@ -563,15 +578,16 @@

    When checking permission to use the API, a user agent SHOULD present the user with a choice to - grant access to their precise location, their approximate location, - or to deny the request. A user agent MAY also suggest time-based - [=permission=] [=permission/lifetimes=], such as "24 hours", "1 - week", or choose to remember the permission [=permission/grant=] - indefinitely. However, it is RECOMMENDED that a user agent prioritize - restricting the [=permission=] [=permission/lifetime=] to a single - session: This can be, for example, until the [=environment settings - object/realm=] is destroyed, the end-user [=navigates=] away from the - [=origin=], or the relevant browser tab is closed. + grant access to their [=precise position=], their [=approximate + position=], or to deny the request. A user agent MAY also suggest + time-based [=permission=] [=permission/lifetimes=], such as "24 + hours", "1 week", or choose to remember the permission + [=permission/grant=] indefinitely. However, it is RECOMMENDED that a + user agent prioritize restricting the [=permission=] + [=permission/lifetime=] to a single session: This can be, for + example, until the [=environment settings object/realm=] is + destroyed, the end-user [=navigates=] away from the [=origin=], or + the relevant browser tab is closed.

    @@ -579,13 +595,14 @@

    Preventing Precise Location Reconstruction

    - A malicious actor could potentially infer a user's precise location - by collecting and correlating multiple, distinct approximate - locations. To mitigate this risk of a refinement attack, when a site - receives an approximate location, any subsequent calls from that same - site within a user-agent-defined time window SHOULD return the exact - same, cached approximate location data. A user agent might, for - example, use a time window of 15 minutes. + A malicious actor could potentially infer a user's [=precise + position=] by collecting and correlating multiple, distinct + [=approximate position=]s. To mitigate this risk of a refinement + attack, when a site receives an [=approximate position=], any + subsequent calls from that same site within a user-agent-defined time + window SHOULD return the exact same, cached [=approximate position=] + data. A user agent might, for example, use a time window of 15 + minutes.

    @@ -662,9 +679,9 @@

    A {{GeolocationPosition}}, initialized to null. It's a reference - to the last acquired precise position and serves as a cache. A - user agent MAY evict {{Geolocation/[[cachedPrecisePosition]]}} by - resetting it to null at any time for any reason. + to the last acquired [=precise position=] and serves as a cache. + A user agent MAY evict {{Geolocation/[[cachedPrecisePosition]]}} + by resetting it to null at any time for any reason. @@ -674,8 +691,8 @@

    A {{GeolocationPosition}}, initialized to null. It's a reference - to the last acquired approximate position and serves as a cache. - A user agent MAY evict + to the last acquired [=approximate position=] and serves as a + cache. A user agent MAY evict {{Geolocation/[[cachedApproximatePosition]]}} by resetting it to null at any time for any reason. @@ -882,7 +899,7 @@

  • If |options|.{{PositionOptions/accuracyMode}} is "approximate", then run |handle permission denial|.
  • -
  • Else: +
  • Otherwise:
    1. Let |approximatePermission| be [=get the current permission state=] of "geolocation-approximate". @@ -985,7 +1002,7 @@

    2. If |precisePermission| is "granted", set |effectiveAccuracy| to "precise".
    3. -
    4. Else if |approximatePermission| is "granted", set +
    5. Otherwise, if |approximatePermission| is "granted", set |effectiveAccuracy| to "approximate".
    @@ -1713,8 +1730,8 @@

    feature/default allowlist=] is [=default allowlist/'self'=].

    - The "geolocation" policy controls access to precise location, while - "geolocation-approximate" controls access to approximate location. + The "geolocation" policy controls access to [=precise position=], while + "geolocation-approximate" controls access to [=approximate position=]. Granting "geolocation" permission implicitly grants "geolocation-approximate" permission as well.

    From 5c9f878156f4d66da663f19594bd770b7299bd9e Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Tue, 9 Sep 2025 16:18:35 -0700 Subject: [PATCH 6/9] chore: Tidy HTML --- index.html | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index bd7aebf..3dd9b34 100644 --- a/index.html +++ b/index.html @@ -105,7 +105,7 @@

    An approximate position is a less precise, privacy-preserving representation of the user's location. Instead of - providing precise coordinates, the user agent might return a location + providing precise coordinates, the user agent MUST return a location that is intentionally coarsened to a larger area, such as the nearest city, postal code, or a predefined region. This is often sufficient for applications that don't need to know the user's [=precise position=], @@ -116,10 +116,9 @@

    An approximate location information source is a [=location - information source=] that provides an [=approximate position=]. For - example, the user agent itself can be an [=approximate location - information source=] by taking a [=precise position=] and coarsening - it. + information source=] that returns positions that have been + intentionally obfuscated to make it more difficult to recover the true + location.

    If an end user [=check permission|grants permission=], @@ -458,12 +457,12 @@

    An end-user will generally give [=express permission=] through a user - interface. With the introduction of [=approximate position=], [=User - agents=] with support for [=approximate position=] SHOULD allow the - user to choose between sharing a [=precise position=] or an - [=approximate position=]. This choice gives users more control over - their privacy, allowing them to share a less precise location when - they are not comfortable sharing their [=precise position=]. + interface. [=User agents=] with support for [=approximate position=] + SHOULD allow the user to choose between sharing a [=precise + position=] or an [=approximate position=]. This choice gives users + more control over their privacy, allowing them to share a less + precise location when they are not comfortable sharing their + [=precise position=].

    The user interface will usually present a range of permission @@ -996,7 +995,7 @@

  • -
  • Else if |options|.{{PositionOptions/accuracyMode}} is +
  • Otherwise, if |options|.{{PositionOptions/accuracyMode}} is "precise":
    1. If |precisePermission| is "granted", set @@ -1062,7 +1061,7 @@

  • -
  • Else if |effectiveAccuracy| is "approximate": +
  • Otherwise, if |effectiveAccuracy| is "approximate":
    1. Set |cachedPosition| to [=this=]'s {{Geolocation/[[cachedApproximatePosition]]}}. @@ -1215,7 +1214,7 @@

  • -
  • Else if |effectiveAccuracy| is "approximate": +
  • Otherwise, if |effectiveAccuracy| is "approximate":
    1. Set [=this=]'s {{Geolocation/[[cachedApproximatePosition]]}} to From 11c6d1380ada24c35db556aea6069e00f432ba2f Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Fri, 19 Sep 2025 15:49:07 -0700 Subject: [PATCH 7/9] fix: Address review comments --- index.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 3dd9b34..1cd79d1 100644 --- a/index.html +++ b/index.html @@ -410,11 +410,12 @@

      Alternatively, the API can be disabled in a first-party context by - specifying an HTTP response header: + specifying an HTTP response header. Disabling + `geolocation-approximate` will also disable `geolocation`.

      @@ -441,7 +442,10 @@

      a more privacy-friendly alternative. When an application requests an [=approximate position=], the user agent can provide a less precise location that still meets the application's needs while protecting the - user's [=precise position=]. + user's [=precise position=]. Even when a [=precise position=] is + requested, the user agent can provide an [=approximate position=] + instead, for example, if the user has only granted permission for + approximate accuracy.

      @@ -572,7 +576,8 @@

      permission|checks permission=] for the level of accuracy specified by the {{PositionOptions/accuracyMode}} option. A grant for the "geolocation" permission implies a grant for the - "geolocation-approximate" permission. + "geolocation-approximate" permission, while if the + "geolocation-approximate" is denied, then so is "geolocation".

      When checking permission From 9578188a62d0fa8f6a1d71ef0801f460956904eb Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Fri, 19 Sep 2025 15:53:07 -0700 Subject: [PATCH 8/9] fix: Address review comment --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 1cd79d1..3d8caf5 100644 --- a/index.html +++ b/index.html @@ -1737,7 +1737,8 @@

      The "geolocation" policy controls access to [=precise position=], while "geolocation-approximate" controls access to [=approximate position=]. Granting "geolocation" permission implicitly grants - "geolocation-approximate" permission as well. + "geolocation-approximate" permission as well, while restricting + "geolocation-approximate" also restricts "geolocation".

      From 3fa88696bd8745bbc42d23bb96a2dfdf0937cb94 Mon Sep 17 00:00:00 2001 From: Alvin Ji Date: Mon, 22 Sep 2025 15:07:27 -0700 Subject: [PATCH 9/9] feat: Define custom permission flow for geolocation --- index.html | 100 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index 3d8caf5..888c667 100644 --- a/index.html +++ b/index.html @@ -562,7 +562,8 @@

      - Checking permission to use the API + Checking permission to use the + API

      Geolocation is a [=default powerful feature=] identified @@ -572,24 +573,87 @@

      for [=approximate position=].

      - When a website requests location access, the user agent [=check - permission|checks permission=] for the level of accuracy specified by - the {{PositionOptions/accuracyMode}} option. A grant for the - "geolocation" permission implies a grant for the - "geolocation-approximate" permission, while if the - "geolocation-approximate" is denied, then so is "geolocation". + To request geolocation permission for a given + {{PermissionDescriptor}} |descriptor|, the user agent MUST run the + following steps. These steps override the default behavior of + [=request permission to use=] for these features.

      +
        +
      1. If |descriptor|.{{PermissionDescriptor/name}} is "geolocation": +
          +
        1. Prompt the user to choose between granting permission for + [=precise position=], granting permission for [=approximate + position=], or denying permission. +
        2. +
        3. If the user grants permission for [=precise position=]: +
            +
          1. Set the permission state of "geolocation" to "granted". +
          2. +
          3. Set the permission state of "geolocation-approximate" to + "granted". +
          4. +
          5. Return "granted". +
          6. +
          +
        4. +
        5. If the user grants permission for [=approximate position=]: +
            +
          1. Set the permission state of "geolocation-approximate" to + "granted". +
          2. +
          3. Return "prompt". +
          4. +
          +
        6. +
        7. If the user denies permission: +
            +
          1. Set the permission state of "geolocation" to "denied". +
          2. +
          3. Set the permission state of "geolocation-approximate" to + "denied". +
          4. +
          5. Return "denied". +
          6. +
          +
        8. +
        +
      2. +
      3. If |descriptor|.{{PermissionDescriptor/name}} is + "geolocation-approximate": +
          +
        1. Prompt the user to choose between granting permission for + [=approximate position=] or denying permission. +
        2. +
        3. If the user grants permission for [=approximate position=]: +
            +
          1. Set the permission state of "geolocation-approximate" to + "granted". +
          2. +
          3. Return "granted". +
          4. +
          +
        4. +
        5. If the user denies permission: +
            +
          1. Set the permission state of "geolocation" to "denied". +
          2. +
          3. Set the permission state of "geolocation-approximate" to + "denied". +
          4. +
          5. Return "denied". +
          6. +
          +
        6. +
        +
      4. +

      - When checking permission - to use the API, a user agent SHOULD present the user with a choice to - grant access to their [=precise position=], their [=approximate - position=], or to deny the request. A user agent MAY also suggest - time-based [=permission=] [=permission/lifetimes=], such as "24 - hours", "1 week", or choose to remember the permission - [=permission/grant=] indefinitely. However, it is RECOMMENDED that a - user agent prioritize restricting the [=permission=] - [=permission/lifetime=] to a single session: This can be, for - example, until the [=environment settings object/realm=] is + A user agent MAY also suggest time-based [=permission=] + [=permission/lifetimes=], such as "24 hours", "1 week", or choose to + remember the permission [=permission/grant=] indefinitely. However, + it is RECOMMENDED that a user agent prioritize restricting the + [=permission=] [=permission/lifetime=] to a single session: This can + be, for example, until the [=environment settings object/realm=] is destroyed, the end-user [=navigates=] away from the [=origin=], or the relevant browser tab is closed.

      @@ -881,7 +945,7 @@

    2. [=In parallel=]:
        -
      1. Set |permission| to [=request permission to use=] +
      2. Set |permission| to [=request geolocation permission=] for |descriptor|.
      3. Let |handle permission denial| be the following steps: