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

Posted

技术标签:

【中文标题】当 Mac 设置(不是浏览器设置)拒绝位置共享时,HTML5 Geolocation API 悄然失败【英文标题】:HTML5 Geolocation API quietly fails when Mac settings (not browser settings) deny location sharing 【发布时间】:2021-01-02 20:27:29 【问题描述】:

我遇到了一个问题,如果我的 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 触发的原生弹出窗口征求你的同意。现在,即使您单击允许,因为系统偏好不允许它,它也不会共享,代码不会记录任何内容。但至少这是你第一次看到正在发生的事情。在随后的点击中,无论是浏览器还是代码都不会发生任何事情。再次,我更关心代码,只是指出我承认浏览器向您展示了一些东西

【问题讨论】:

我完全在指定版本的操作系统和浏览器上,我运行了你的代码,但它工作正常。我在浏览器 URL 的右侧收到一条错误消息“Mac 系统偏好设置中的位置服务已关闭。 @raj240 但在控制台中,没有任何记录对吗?代码失败了 【参考方案1】:

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

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

兼容性:http://caniuse.com/#feat=permissions-api

虽然请注意,这仅表示浏览器级别的权限。即使授予了此权限,像操作系统这样的另一层仍然可以稍后在链中拒绝它。

鉴于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 详细说明浏览器必须在成功/错误时调用回调函数,浏览器本身不太可能将其静音。如果没有提供超时,操作系统更有可能决定从不响应而不是拒绝访问,从而使函数无限期地处于挂起状态。

【讨论】:

【参考方案2】:

试试这个:

// 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>

【讨论】:

只需将timeout 作为选项传递给navigator.geolocation.getCurrentPosition 即可实现相同的行为。有关解释和示例,请参见先前的答案。 旁注:setTimeout 非常不准确,因为它受到同步并行运行的逻辑的影响,或者其他操作也异步排队。对于这些时间很重要的情况,应该使用setInterval

以上是关于当 Mac 设置(不是浏览器设置)拒绝位置共享时,HTML5 Geolocation API 悄然失败的主要内容,如果未能解决你的问题,请参考以下文章

XAMPP 拒绝在 Mac os 10.8 上启动 Apache

在 Mac 上设置 gvm 时权限被拒绝

PhotoLibrary 访问 .notDetermined,当拒绝时启用

如何配置Mac OS X 与 Windows之间共享网络

检查浏览器是不是在操作系统级别阻止了位置设置

windows访问NFS出错