当 Mac 设置(不是浏览器设置)拒绝位置共享时,HTML5 地理定位 API 悄悄失败

HTML5 Geolocation API quietly fails when Mac settings (not browser settings) deny location sharing

我 运行 遇到一个问题,如果我的 Mac 拒绝位置共享,那么 JS 代码中什么也不会发生...这很危险,无论如何要解决这个问题?如果系统拒绝位置共享,我希望抛出异常

运行 macOS Mojave 10.14.6 并在 Chrome 87.0.4280.88.

中测试

System Preferences > Security & Privacy中,您可以勾选Enable Location Services,然后勾选允许的应用程序。如果我完全取消选中 Enable Location Services 或保持选中状态但取消选中 Google Chrome,则代码会悄悄失败*,我的意思是在下面的示例代码中没有任何内容记录到控制台。

代码:

$("#btn").click(function(e){
  e.preventDefault();
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      # success
      function(location) {
        console.log("GPS coordinates retrieved")
      }, 
      # failure
      function(error) {
        console.log(error.message)
      }
    )
  } else {
    console.log("GPS not supported by browser")
  }
})

是否有一种元方法可以捕捉到这一点(例如,确定位置是否已在 JS 的系统首选项中启用)以便记录某些内容并且我可以继续代码中的下一步?

*代码悄悄地失败了,但从技术上讲,浏览器有办法提醒你,但只是第一次。所以基本上,假设您的浏览器没有阻止 URL 并且您使用的是 HTTPS(例如,满足所有浏览器条件),第一次单击 #btn 时,您将获得本机弹出窗口navigator.geolocation.getCurrentPosition 触发请求您的许可。现在,即使您单击允许,因为系统偏好设置不允许,它也不会共享,代码不会记录任何内容。但至少这是你第一次看到正在发生的事情。在随后的点击中,浏览器或代码中都不会发生任何事情。同样,我更关心代码,只是指出我承认浏览器会向您展示一些东西

在某些浏览器上,您可以在不提示用户的情况下检查权限状态,如下所示:

const permissionStatus = await navigator.permissions.query({ name: 'geolocation' })

Compatibility: http://caniuse.com/#feat=permissions-api

但请注意,这仅表示浏览器级别的权限。即使授予此权限,OS 等另一层仍然可以在链的后面拒绝它。

鉴于 navigator.geolocation.getCurrentPosition() always immediately returns with undefined 无论如何,处理这些情况的最佳方法是 timeout 强制在给定持续时间后调用错误回调,如果没有响应提供给浏览器。幸运的是,您可以通过将 options 作为函数的第三个参数传递给函数来轻松地做到这一点。

const options = { timeout: 5000 }
navigator.geolocation.getCurrentPosition(successCb, errorCb, options)

你的情况:

$("#btn").click(function(e){
  e.preventDefault()

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      // success
      function(location) {
        console.log("GPS coordinates retrieved")
      }, 
      // failure
      function(error) {
        console.log(error.message)
      },
      {
        timeout: 5000 // ms
      }
    )
  } else {
    console.log("GPS not supported by browser")
  }
})

考虑到 W3C specification 浏览器 必须 调用 success/error 上的回调函数的详细信息,不太可能被浏览器本身静音。 OS 更有可能决定永远不响应而不是拒绝访问,如果没有提供超时,则函数无限期地处于挂起状态。

试试这个:

// pseudo
set location to unknown (yet)
set waiting time
try retrieve location
  set location when receive, call as resolved (success)
  set error when failure, call resolved (failure)

cancel waiting when receive (location / a sure failure)
resolveLocation after timeout, success or failure

$("#btn").click(function(e){
  e.preventDefault()
  function resolveLocation(){
    if(location){
      console.log("Location detected",location)
    }else{
      console.log("Cannot detect location")
    }
  }
  var location = null;
  var wait = setTimeout(resolveLocation, 3000)
  if(navigator.geolocation){
    navigator.geolocation.getCurrentPosition(
      // success
      function(_location){
        clearTimeout(wait)
        console.log("GPS coordinates retrieved")
        location = _location;
        resolveLocation()
      },
      // error
      function(error){
        clearTimeout(wait)
        console.log("GPS error")
        resolveLocation()
      }
    )
  }else{
    clearTimeout(wait)
    console.log("GPS not supported by browser")
    resolveLocation()
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Check</button>