script 标签中 crossorigin 属性说明

1. 背景知识:script 标签有哪些属性

  1. src : 脚本来源地址

  2. type: 表示所代表的脚本类型,有如下取值:

    • 未设置(默认)/一个空字符串/一个 JavaScript MIME 类型:普通脚本/传统脚本
    • module:模块脚本
    • importmap:代表元素体内包含导入映射(importmap)表
    • 任何其他值:所嵌入的内容被视为一个数据块,不会被浏览器处理
  3. integrity:包含用户代理可用于验证所获取到资源的完整性的内联元数据

  4. crossorigin:见下文详解

  5. 其他属性,比如async defer等,与本文关系不大,详见MDN

2. crossorigin 属性的作用

1. 【html 标准】文档中的解释

对于普通脚本,它控制是否公开错误信息。对于模块脚本,它控制用于跨域请求的凭据模式。(原文见此

2. 个人总结

默认情况下,可以从任意位置加载 js 。

如果配置了该属性,会让浏览器启用CORS检查(判断响应头中Access-Control-Allow-Origin是否与当前文档同源),如果检查不通过,则浏览器拒绝执行代码。

什么情况下要启用检查?

  1. 需要捕获跨域脚本中的错误信息
  2. type="module" 并且需要发送凭据

只验证了 cookie,其他认证信息不知道怎么测试

  1. 需要校验跨域脚本的完整性(配置了integrity的时候)
  2. 文档中的脚本(并不单单指跨域脚本)需要使用 跨域隔离的 API(比如SharedArrayBuffer,原因见此

3. 不设置 crossorigin 属性时的表现

  1. 如果没有设置type="module":获取资源时,是否带上 cookie,取决于 cookie 的同站策略(比同源策略更广泛,跨域时也可能同站

不跨站就会发 cookie;跨站的话,只有满足SameSite=None; Secure=true 的 cookie 才会发送

  1. 如果设置了type="module",则一定不发送 cookie

  2. 收到响应后,浏览器会正常执行脚本代码

  3. 全局错误捕获方面

    • 页面同源的脚本出错:

      • promise 异常:可以捕获✅
      • 其他普通异常:可以捕获✅
    • 跨域脚本出错:

      • promise 异常:无法捕获❌
      • 其他普通异常:只能捕获到Script error.的错误,没有详细信息

4. 设置了 crossorigin 属性时的表现

crossorigin 有 2 个值:use-credentialsanonymous

只有指定use-credentials时才表现为use-credentials,其他任何不合法的值都视为 anonymous

1. anonymous

  1. 不管有没有设置type="module",获取资源时,都不会发送 cookie

  2. 收到响应后,只有满足 响应头中Access-Control-Allow-Origin === 请求头中的origin字段,浏览器才会执行脚本代码,否则会抛出 CORS 异常

  3. 如果设置了integrity,就还会再校验一次资源完整性,不满足也会报错

  4. 全局错误捕获方面

    • 页面同源的脚本出错:

      • promise 异常:可以捕获✅
      • 其他普通异常:可以捕获✅
    • 跨域脚本出错:

      • promise 异常:可以捕获✅
      • 其他普通异常:可以捕获✅

2. use-credentials

  1. 不管有没有设置type="module",发送请求时是否带上 cookie,都取决于 cookie 的同站策略

  2. 收到响应后,响应头中需要同时满足以下条件,浏览器才会执行脚本代码,否则会抛出 CORS 异常:

    1. Access-Control-Allow-Origin === 请求头中的origin字段
    2. Access-Control-Allow-Credentials为 true
  3. 如果设置了integrity,就还会再校验一次资源完整性,不满足也会报错

  4. 错误捕获方面,与anonymous相同

5. 深入错误捕获

1. 网络解释

原文见此

2.【html 标准】文档中的说明

1. 普通异常为什么会抛出Script error.

html 标准文档 中有这样一句话:

如果该脚本属于classic scriptmuted errors为 true,则普通异常就会抛出Script error.

  1. 什么是classic script

就是指普通脚本。

省略type属性,或将其设置为空字符串,或将其设置为JavaScript 的MIME类型,意味着该脚本是classic script原文见此

  1. muted errors什么情况下会为 true?

跨域脚本就为 true,后面附带了一句极为简单的解释:“会泄漏私有信息”。(原文见此

  1. 为什么设置了crossorigin就可以暴露错误详情?

以下为个人理解,没有找到原文。

因为设置了之后,浏览器会校验响应头(Access-Control-Allow-Origin),发现服务器是允许页面内访问该脚本资源的,于是允许暴露错误信息。

2. 全局 Promise 异常为什么无法捕获?

  1. 全局 Promise 异常是HostPromiseRejectionTracker进行处理的(见下图标注序号 1 处,原文见此

  1. HostPromiseRejectionTracker内部,判断如果是classic script,则直接 return (原文见此

(个人理解同源的情况下应该走到第 5 小点中,就可以正常捕获)

  1. 回到 1 中截图标识 2 的位置,提到:

If a rejection is still not handled after this, then the rejection may be reported to a developer console.

由于HostPromiseRejectionTracker方法未进行任何处理,没有被全局监听所捕获,于是就在控制台抛出了异常。

3. 源码分析:跨域脚本抛出的普通异常为什么没有详细堆栈

1. v8 源码

首先,在 v8 源码中,script脚本默认就是跨域的。注释提到会在“主线程合并期间修复”(未研究对应代码)

上图中的注释下面调用了ScriptOriginOptions方法进行初始化script,其第一个参数就是表示是否为跨域脚本,传入的值是 false

而异常信息的IsSharedCrossOrigin()方法就是直接调用scriptIsSharedCrossOrigin()方法

因此,可以知道, 异常信息的IsSharedCrossOrigin()方法会默认返回 false.

2. chromium 源码

在chromium源码中,如果异常信息的IsSharedCrossOrigin()方法返回 false,会将sanitize_script_errors设为SanitizeScriptErrors::kSanitize

之后,判断到满足sanitize_script_errors == SanitizeScriptErrors::kSanitize,则调用CreateSanitizedError方法

就是这个方法里,会抛出Script error,并且没有给出堆栈信息。

以上可以解释普通异常为什么没有堆栈,至于 Promise 异常为什么不能捕获,还没有看懂。并且限于水平,未能调试该源码,不太好说这些没有错漏之处,颇为遗憾……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值