一. 前言
Tampermonkey is one of the most popular browser extension with over 10 million users. It's available for Chrome, Microsoft Edge, Safari, Opera Next, and Firefox.
It allows its users to customize and enhance the functionality of your favorite web pages. Userscripts are small JavaScript programs that can be used to add new features or modify existing ones on web pages. With Tampermonkey, you can easily create, manage, and run these userscripts on any website you visit.
本文以Documentation | Tampermonkey内容展开, 将对其中内容进行翻译和注释, 以及实际的使用例子.
1.1 操作页面
测试环境:
- 浏览器: edge_115
- TamperMonkey: 4.19.0
#
, 表示脚本注入的顺序, 可以修改, 按照创建的先后来确定.
在setting
页面设置为高级, 一些高阶的菜单才会显示出来, 如编辑器设置, 安全设置等等.
例如下载文件会用到的:
假如文件格式不在这个列表中, 将无法下载.
由于设置页面内容极多, 这里不一一赘述.
1.2 基本原理
执行一个tampermonkey
脚本, 自定义的脚本 => tampermonkey
(管理) => (注入) => 干预网页.
这个过程涉及到一些列的安全(限制)问题, 例如同源安全策略, 内容安全策略, 沙盒机制等.
- 浏览器的同源策略 - Web 安全 | MDN (mozilla.org)
- 『译』window.close 限制 - 掘金 (juejin.cn)
- HTTP中的CSP ( Content Security Policy )内容安全策略 - 掘金 (juejin.cn)
而Tampermonkey
可以突破其中的一些限制, 为用户高度自定义网页提供可能, 这种能力是浏览器赋予所有扩展的, 并不是Tampermonkey
做了什么才实现的, 但是制作浏览器扩展却是颇为麻烦的事情, 假如需要发布给他人使用, 还得上传到chrome store
, 这又是一堆繁琐的事情.Tampermonkey
站出来了, 将其中的一些扩展的特性封装成为若干的api
接口, 使用者只需要在脚本中简单调用即可实现扩展才能实现的功能, 这大大简化了为特定的站点开发脚本和发布成本.
Q200: Is it possible to install scripts as native Chrome extension even though Tampermonkey is installed?
A200 Yes it is. If you want to install a script just click at the install link. Tampermonkey now asks you whether to install the script in Tampermonkey or native in Chrome.
但chrome v3协议的发布(Chrome Manifest V3 - 披着羊皮的狼? - 知乎 (zhihu.com)), 相关的权限的收紧, 对于Tampermonkey
影响颇大(甚至危及Tampermonkey), 该协议的争议颇多 以及其他的一些原因, Google目前暂缓该协议的执行.
1.3 沙箱
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// ==/UserScript==
console.log(this);
从安全角度来看, 理论上tampermonkey
应当实现脚本相对独立运行的隔离机制(sandbox
), 即简单理解就是从用户脚本可以访问到(甚至可以修改)页面的元素或者变量, 但是反过来不能( 或者是有限访问, 或者是只能间接 )让页面的脚本访问到用户脚本的资源, 避免页面脚本(经过特意构造相关用户脚本函数陷阱)来劫持用户脚本, 或者是页面脚本读取了用户脚本相关的信息.
tampermonkey
中以下header
和api
和以上机制相关:
但是关于上述内容的文档, 并没有很细致, 甚至没有具体提及其中的细节, 理解起来颇为不易, 几乎无法检索到相关内容.
1.4 访问本地脚本
Q204: How can I allow Tampermonkey access to local file URIs?
A204 File access for Tampermonkey is only available at Chrome and derivates. In order to enable it open Google Chrome and click on the "More" icon (the three dots) in the top-right corner of the window. From the menu, select "More tools" and then click on "Extensions".
This will open the Chrome extensions page (
chrome://settings/
), which shows a list of all the extensions that are installed in your browser. Find Tampermonkey and click on the "Details" link to the right of its name.On the extension's details page, scroll down to the "Permissions" section. Here, you will see a list of all the permissions that the extension has been granted. To enable file access, you will need to check the box next to the "Allow access to file URLs" permission. [( video tutorial)](javascript:void(0))
扩展管理, 找到tampermonkey
, 详情页面 => 允许访问文件urls
.
// 在脚本中添加
@require //local/file.js
- You can try TamperDAV
- You can try the Tampermonkey Editors extension to edit the script at vscode.dev
还有个DEV
版本, 可以进一步和本地编辑器(vscode
)整合在一起.
二. Userscript Header
header | 作用 | 重要程度 | 备注 |
---|---|---|---|
@name | 脚本名称 | ||
@namespace | 脚本命名空间 | ||
@copyright | 版权声明 | ||
@version | 版本号 | 影响脚本的更新发布 | |
@description | 脚本描述 | ||
@icon, @iconURL, @defaulticon | 脚本ico | ||
@icon64, @icon64URL | 脚本ico, base64格式 | ||
@grant | 批准api | 1 | |
@author | 脚本作者 | ||
@homepage, @homepageURL, @website, @source | 作者主页 | ||
@antifeature | 商业行为 | ||
@require | 外部脚本请求 | 1 | |
@resource | 外部资源请求 | 1 | |
@include | 脚本适配的url, 宽泛 | 1 | 未来受到v3协议影响最大的 |
@match | 脚本适配url, 窄 | 1 | |
@exclude | 脚本排除url | 1 | |
@run-at | 脚本注入的时间点 | 1 | |
@sandbox | 沙箱模式 | ||
@connect | 跨域请求host | 1 | 不要设置为全部站点 |
@noframes | 脚本运行是否受到子框架的触发 | 1 | |
@updateURL | 脚本的更新地址url, 需要搭配版本号使用 | ||
@downloadURL | 脚本的更新地址url | 1 | |
@supportURL | 脚本bug反馈, 赞助支持 | ||
@webRequest | 监听匹配规则的url web请求拦截 | 实验性api, 不稳定 | |
@unwrap | 注入的脚本是否进行包裹 |
这是一个调用了大部分TamperMonkey
的header
和api
的脚本的头部示例.
// ==UserScript==
// @name zhihu optimizer
// @namespace https://github.com/Kyouichirou
// @version 3.5.4.5
// @description now, I can say this is the best GM script for zhihu!
// @author HLA
// @run-at document-start
// @match https://*.zhihu.com/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_listValues
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @grant GM_deleteValue
// @grant GM_unregisterMenuCommand
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_openInTab
// @grant GM_getTab
// @grant GM_getTabs
// @grant GM_download
// @grant GM_saveTab
// @grant GM_info
// @grant window.onurlchange
// @grant window.close
// @connect www.zhihu.com
// @connect lens.zhihu.com
// @connect api.zhihu.com
// @connect cn.bing.com
// @connect img.meituan.net
// @connect www.cnblogs.com
// @require https://cdn.staticfile.org/xlsx/0.10.0/xlsx.core.min.js
// @icon https://static.zhihu.com/heifetz/favicon.ico
// @compatible chrome 80+
// @license MIT
// @noframes
// @note cent browser(https://www.centbrowser.com/) is recommended, and edeg browser is not recommended, too bloated and jumbled
// @note just test on chrome 80+ // 非标准
// ==/UserScript==
相关头部元数据在greasyfork
读取情况, Script meta keys read by Greasy Fork.
2.1 基本信息
2.1.1 @name
脚本名称
The name of the script.
Internationalization is done by adding an appendix naming the locale.
// @name A test
// @name:de Ein Test
2.1.2 @namespace
脚本命名空间
The namespace of the script.
@namespace
与@name
这两个属性被作为脚本的唯一标识符, 用户脚本管理器根据它们来判断一个脚本是否已安装. Sleazy Fork 也需要这些属性, 若用户在更新脚本时改变了两者中的任意一项, 将发出警告.
2.1.3 @copyright
版权声明
A copyright statement shown at the header of the script's editor right below the script name.
2.1.4 @version
版本号(重要), 按照下面的规范来写, 脚本检查更新的关键
The script version. This is used for the update check and needs to be increased at every update.
In this list the next entry is considered to be a higher version number, eg: Alpha-v1
< Alpha-v10
and 16.4
== 16.04
Alpha-v1
Alpha-v10
Alpha-v2
Beta
0.5pre3
0.5prelimiary
0.6pre4
0.6pre5
0.7pre4
0.7pre10
1.-1
1
==1.
==1.0
==1.0.0
1.1a
1.1aa
1.1ab
1.1b
1.1c
1.1.-1
1.1
==1.1.0
==1.1.00
1.1.1.1.1
1.1.1.1.2
1.1.1.1
1.10.0-alpha
1.10
==1.10.0
1.11.0-alpha
1.11.0-alpha.1
1.11.0-alpha+1
1.11.0-0.3.7
1.12+1
==1.12+1.0
1.12+1.1
==1.12+1.1.0
1.12+2
1.12+2.1
1.12+3
1.12+4
1.12
2.0
16.4
==16.04
2.1.5 @description
脚本描述
A short significant description.
Internationalization is done by adding an appendix naming the locale.
// @description This userscript does wonderful things
// @description:de Dieses Userscript tut wundervolle Dinge
2.1.6 @icon, @iconURL, @defaulticon
脚本的图表
The script icon in low res.
2.1.7 @icon64, @icon64URL
脚本图表, base64
This scripts icon in 64x64 pixels. If this tag, but @icon
is given the @icon
image will be scaled at some places at the options page.
2.1.8 @author
脚本作者
The scripts author.
2.1.9 @homepage, @homepageURL, @website, @source
The authors homepage that is used at the options page to link from the scripts name to the given page. Please note that if the @namespace
tag starts with http://
its content will be used for this too.
项目页面, 或者时作者的个人页面.
2.1.10 @antifeature
披露会为脚本作者而非用户带来额外收益的功能. 你可以通过在脚本的元数据中添加
@antifeature type description
值的方法披露. 其中type( 类型) 是必须填写的内容, 而 description( 描述) 则可以留空. 点击此处了解 Sleazy Fork 读取的 type( 类型) 的相关信息.
This tag allows script developers to disclose whether they monetize their scripts. It is for example required by GreasyFork.
Syntax:
- ads
- tracking
- miner
// @antifeature ads We show you ads
// @antifeature:fr ads Nous vous montrons des publicités
// @antifeature tracking We have some sort of analytics included
// @antifeature miner We use your computer's resources to mine a crypto currency
Internationalization is done by adding an appendix naming the locale.
2.1.11 @updateURL
脚本更新源: url, 需要设置版本号.
An update URL for the userscript. Note: a @version
tag is required to make update checks work.
2.1.12 @downloadURL
检测到更新时将从其中下载脚本的URL. 如果使用值none, 则不会进行更新检查.
Defines the URL where the script will be downloaded from when an update was detected. If the value none is used, then no update check will be done.
2.1.13 @supportURL
支持页面, 例如bug上报, 个人赞助.
Defines the URL where the user can report issues and get personal support.
2.2 @grant
批准脚本的使用哪些api
接口.
@grant
is used to whitelist GM_*
and GM.*
functions, the unsafeWindow
object and some powerful window
functions.
// @grant GM_setValue
// @grant GM_getValue
// @grant GM.setValue
// @grant GM.getValue
// @grant GM_setClipboard
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @grant window.onurlchange
Since closing and focusing tabs is a powerful feature this needs to be added to the @grant
statements as well. In case @grant
is followed by none
the sandbox is disabled. In this mode no GM_*
function but the GM_info
property will be available.
默认状态下, @grant none
, 意味着沙箱是被禁用的, 除了GM_info之外, 任意的GM_*()
函数都将被禁用.
window.close()
, window.focus()
需要专门声明相关的权限.
// @grant none
If no @grant
tag is given an empty list is assumed. However this different from using none
.
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @license MIT
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
document.onclick = () => unsafeWindow.close(); // 这种情况下unsafewindow是可以访问到的, 只是无法关闭标签
})();
注意@grant none
和没有@grant
是不同的, 后者不存在的时候, 是可以访问到unsafeWindow
.
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @grant none
// @license MIT
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
document.onclick = () => unsafeWindow.close(); // 这种情况下unsafewindow是访问不到的
})();
将无法访问到对象
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @grant window.close
// @license MIT
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
'use strict';
document.onclick = () => window.close(); // 可以正常执行, 也是唯一可以关闭标签的方式
})();
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @grant window.close
// @license MIT
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
'use strict';
document.onclick = () => unsafeWindow.close(); // 可以访问unsafewindow, 但是无法关闭窗体
})();
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @grant GM_setValue
// @license MIT
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
'use strict';
document.onclick = () => unsafeWindow.close(); // 可以访问unsafewindow, 但是无法关闭窗体
})();
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @license MIT
// @grant GM_log
// @grant unsafeWindow
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
(function() {
document.onclick = () => unsafeWindow.close(); // 无法关闭
})();
2.3 @require
引入外部的脚本, 如一些第三方库.
Points to a JavaScript file that is loaded and executed before the script itself starts running. Note: the scripts loaded via @require
and their "use strict" statements might influence the userscript's strict mode!
// @require https://code.jquery.com/jquery-2.1.4.min.js
// @require https://code.jquery.com/jquery-2.1.3.min.js#sha256=23456...
// @require https://code.jquery.com/jquery-2.1.2.min.js#md5=34567...,sha256=6789...
// @require tampermonkey://vendor/jquery.js
// @require tampermonkey://vendor/jszip/jszip.js
Please check the sub-resource integrity section for more information how to ensure integrity.
Multiple tag instances are allowed.
2.4 @resource
重要性: 重要.
引入外部资源( 如 JavaScript, CSS, 图像等) 到脚本. 查看关于引入外部脚本到代码的规则. 如果你使用 SRI( Sub-Resource Integrity) 来记录外部脚本哈希( 更多信息请参阅 TamerMonkey 文档 与 MDN 文档), 当 SRI 与文件的实际哈希不一致时将会在 Sleazy Fork 进行警告.
Preloads resources that can by accessed via GM_getResourceURL
and GM_getResourceText
by the script.
// @resource icon1 http://www.tampermonkey.net/favicon.ico
// @resource icon2 /images/icon.png
// @resource html http://www.tampermonkey.net/index.html
// @resource xml http://www.tampermonkey.net/crx/tampermonkey.xml
// @resource SRIsecured1 http://www.tampermonkey.net/favicon.ico#md5=123434...
// @resource SRIsecured2 http://www.tampermonkey.net/favicon.ico#md5=123434...;sha256=234234...
Please check the sub-resource integrity section for more information how to ensure integrity.
Multiple tag instances are allowed.
2.5 @include
指定在哪些站点上运行脚本, 脚本的运行适配url/
但是由于Google
认为这种模式过于宽泛, 导致运行的效率底下, 在其V3
扩展协议中这种方式将被禁用.
The pages on that a script should run. Multiple tag instances are allowed. @include doesn't support the URL hash parameter. You have to match the path without the hash parameter and make use of window.onurlchange
// @include http://www.tampermonkey.net/*
// @include http://*
// @include https://*
// @include /^https:\/\/www\.tampermonkey\.net\/.*$/
// @include *
Note: When writing something like *://tmnk.net/*
many script developers expect the script to run at tmnk.net
only, but this is not the case. It also runs at https://example.com/?http://tmnk.net/
as well.
Therefore Tampermonkey interprets @includes
that contain a ://
a little bit like @match
. Every *
before ://
only matches everything except :
characters to makes sure only the URL scheme is matched. Also, if such an @include
contains a /
after ://
, then everything between those strings is treat as host, matching everything except /
characters. The same applies to *
directly following ://
.
// ==UserScript==
// @name test_include_match
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @include /https:\/\/blog\.csdn\.net/\w+\/article\/details\/\d+/
// @icon https://www.google.com/s2/favicons?sz=64&domain=csdn.net
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('ok');
})();
这是match做不到的
// @match /https:\/\/blog\.csdn\.net/\w+\/article\/details\/\d+/
// 需要改成这种模式, 但是这种匹配模式相当僵化
// @match https://blog.csdn.net/qq_*/article/details/*
2.6 @match
指定在哪些站点上运行脚本, 脚本的运行适配url, 相对固定的匹配.
In Tampermonkey, the @match
directive is used to specify the web pages that your script should run on. The value of @match
should be a URL pattern that matches the pages you want your script to run on. Here are the parts of the URL pattern that you'll need to set:
// @match <protocol>://<domain><path>
- protocol - This is the first part of the URL, before the colon. It specifies the protocol that the page uses, such as
http
orhttps
.*
matches both. - domain - This is the second part of the URL, after the protocol and two slashes. It specifies the domain name of the website, such as
tmnk.com
. You can use the wildcard character this way*.tmnk.net
to matchtmnk.net
and any sub-domain of it likewww.tmnk.net
. - path - This is the part of the URL that comes after the domain name, and may include additional subdirectories or filenames. You can use the wildcard character
*
to match any part of the path.
Please check this documentation to get more information about match pattern. Note: the <all_urls>
statement is not yet supported and the scheme part also accepts http*://
.
Multiple tag instances are allowed.
More examples:
// @match *://*/*
// @match https://*/*
// @match http://*/foo*
// @match https://*.tampermonkey.net/foo*bar
2.7 @exclude
排除掉哪些适配url
, 优先级别最高, 相比于include
和match
Exclude URLs even it they are included by @include
or @match
.
Multiple tag instances are allowed.
2.8 @run-at
脚本注入的时间点.
Defines the moment the script is injected. In opposition to other script handlers, @run-at
defines the first possible moment a script wants to run. This means it may happen, that a script that uses the @require
tag may be executed after the document is already loaded, cause fetching the required script took that long. Anyhow, all DOMNodeInserted and DOMContentLoaded events that happended after the given injection moment are cached and delivered to the script when it is injected.
注意@require
在请求外部资源时, 可能缓慢, 脚本的注入可能是在页面文档加载完成后执行.
// @run-at document-start
// 尽可能快注入脚本, 通常希望目标页面尚未加载渲染前, 例如需要尽快注入操作的CSS.
The script will be injected as fast as possible.
// @run-at document-body
// 等待页面主体加载完成
The script will be injected if the body element exists.
// @run-at document-end
// 等待DOMContentLoaded事件触发执行, 早一点或者晚一点?
The script will be injected when or after the DOMContentLoaded event was dispatched.
// @run-at document-idle
// 默认状态执行(缺省), DOMContentLoaded触发
The script will be injected after the DOMContentLoaded event was dispatched. This is the default value if no @run-at
tag is given.
// @run-at context-menu
// 右键菜单触发.
需要注意假如使用该header, 将导致所有的include, exclude
的规则将被忽视.
The script will be injected if it is clicked at the browser context menu (desktop Chrome-based browsers only). Note: all @include
and @exclude
statements will be ignored if this value is used, but this may change in the future.
2.9 @sandbox
沙箱机制允许tampermonkey
决定注入脚本到哪个页面中.
@sandbox
allows Tampermonkey to decide where the userscript is injected:
-
MAIN_WORLD - the page
目标页面
-
ISOLATED_WORLD - the extension's content script/
tampermonkey自身的
content
脚本 -
USERSCRIPT_WORLD - a special context created for userscripts.
一个特殊的上下文, 由脚本所创建
But instead of specifying an environment, the userscript can express what exactly it needs access to. @sandbox
supports three possible arguments:
-
raw
"Raw" access means that a script for compatibility reasons always needs to run in page context, the MAIN_WORLD. At the moment this mode is the default if@sandbox
is omitted.这是默认的模式, 假如缺失值. 出于兼容的原因, 脚本总是需要在页面上下文中运行.
-
JavaScript
"JavaScript" access mode means that this script needs access tounsafeWindow
. At Firefox a special context, the USERSCRIPT_WORLD, is created which should also bypass all remaining CSP issues. It however, might create new issues since nowcloneInto
andexportFunction
are necessary to share objects with the page.raw
mode is used as fallback at other browsers.这个模式是专门针对FireFox的?
在
Firefox
中创建了一个特殊的上下文USERSCRIPT_WORLD, 它也应该绕过所有剩余的CSP问题. 然后这又创造了另一问题cloneInto
andexportFunction
, 这需要和页面分享对象. 在页面上下文中执行在其他浏览器中用作回退. -
DOM
Use this access mode if the script only needs DOM and no directunsafeWindow
access. If enabled these scripts are executed inside the extension context, the ISOLATED_WORLD, or at any other enabled context otherwise, because they all grant DOM access.这个模式下, 意味着脚本只需要
DOM
, 而不需要直接的unsafeWindow
访问. 如果启用此设置, 脚本将可以在任意 已启用上下文的环境下执行(?), 因为都批准了DOM
访问权限.There is also an option to configure the available sandbox modes which can be used by userscripts. Attention: any option that enables "DOM" mode is potentially unsecure. Userscripts that run in extension context have almost full extension permissions and can even modify and install new userscripts.
// @sandbox JavaScript
2.10 @connect
当批准@grant GM_xmlhttpRequest
, 选择需要连接的url
的host
// @connect *
// *, 则意味着可以连接任意的host
这个header
是为了解决普通的xhr, fetch
受制于同源安全策略的限制, 无法发出跨域的请求.
例如脚本在baidu.com
页面运行, 需要连接qq.com
上的url
.
This tag defines the domains (no top-level domains) including subdomains which are allowed to be retrieved by GM_xmlhttpRequest
// @connect <value>
<value>
can be:
- a domain name like
example.com
(this will also allow all subdomains). - a subdomain name like
subdomain.example.com
. self
to whitelist the domain the script is currently running at.localhost
to access the localhost.- an IP address like
1.2.3.4
. *
.
If it's not possible to declare all domains a userscript might connect to then it's a good practice to do the following:
- Declare all known or at least all common domains that might be connected by the script to avoid the confirmation dialog for most users.
- Additionally add
@connect *
to the script to allow Tampermonkey to offer an "Always allow all domains" button.
Users can also whitelist all requests by adding *
to the user domain whitelist at the script settings tab.
Notes:
- Both, the initial and the final URL will be checked!
- For backward compatibility to Scriptish
@domain
tags are interpreted as well. - Multiple tag instances are allowed.
More examples:
// @connect tmnk.net
// @connect www.tampermonkey.net
// @connect self
// @connect localhost
// @connect 8.8.8.8
// @connect *
支持多种类型的连接host.
2.11 @noframes
设置脚本只在主页面运行, 不会被iframe子框架触发.
This tag makes the script running on the main pages, but not at iframes.
2.12 @webRequest
// ==UserScript==
// @name GM_webRequest testing
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match *://*/*
// @include *gstatic.com/*
// @include https://google.com/*
// @include *//google.com/*
// @grant GM_webRequest
// @webRequest [{"selector":"*cancel.me/*","action":"cancel"},{"selector":{"include":"*","exclude":"http://exclude.me/*"},"action":{"redirect":"http://new_static.url"}},{"selector":{"match":"*://match.me/*"},"action":{"redirect":{"from":"([^:]+)://match.me/(.*)","to":"$1://redirected.to/$2"}}}]
// ==/UserScript==
console.log("GM_webRequest start");
var currently_active_webrequest_rule = JSON.stringify(GM_info.script.webRequest); // == @webRequst header from above
GM_webRequest([
//{ selector: '*gstatic.com/*', action: 'cancel' },
{ selector: { include: '*gstatic.com/*', exclude: 'http://exclude.me/*' }, action: { redirect: 'https://google.com/' } },
//{ selector: { match: '*://*.gstatic.com*' }, action: { redirect: { from: '([^:]+)://(.+)gstatic.com/(.*)', to: '$1://google.com/$2' } } }
], function(info, message, details) {
console.log(info, message, details); // This also does not work
});
@webRequest
takes a JSON document that matches GM_webRequest
's rule
parameter. It allows the rules to apply even before the userscript is loaded.
2.13 @unwrap
Injects the userscript without any wrapper and sandbox into the page, which might be useful for Scriptlets.
将没有任何包装器和沙箱的用户脚本注入到页面中, 这对于scriptlet
可能很有用.
scriptlet
A scriptlet is a piece of software code that is used by a native Web page scripting language to perform a specific function or process. Scriptlets are primarily implemented in JavaServer Pages (JSP) and include variables, expressions or statements that are used only when requested by a certain client or process.
window["__f__lkptg9op.3c8"] = function () {
with (this) {
(async (u, { p, r, s }) => { try { r(u, s, [undefined, undefined, undefined, p.GM_info, p.unsafeWindow, p.GM]); } catch (e) { if (e.message && e.stack) { console.error("ERROR: Execution of script 'csdn_optimizer_x' failed! " + e.message); console.log(e.stack); } else { console.error(e); } } })(async function (define, module, exports, GM_info, unsafeWindow, GM) {
// ==UserScript==
// @name csdn_optimizer_x
// @namespace https://github.com/Kyouichirou
// @version 1.4.2
// @description make csdn better
// @author HLA
// @include /https:\/\/(\w+\.)?\w+\.csdn\.net.*/
// @license MIT
// @grant GM_info
// @icon https://blog.csdn.net/favicon.ico
// ==/UserScript==
console.log(this.unsafeWindow === unsafeWindow);
console.log(this);
//console.log(globalThis.unsafeWindow === unsafeWindow);
//console.log(this === globalThis);
}, seed)
}
//# sourceURL=chrome-extension://iikmkjmpaadaobahmlepeloendndfphd/userscript.html?name=csdn_optimizer_x.user.js&id=7ee31434-0352-4ac0-82d3-d3bb842ffa70
}
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// @grant none
// @unwrap
// ==/UserScript==
// unsafeWindow, 在这种环境下为undefined
console.log(this);
//# sourceURL=chrome-extension://iikmkjmpaadaobahmlepeloendndfphd/userscript.html?name=New-Userscript.user.js&id=cda33c15-55fb-4e4b-a485-1a44534ef1bd
三. Application Programming Interface
api | 作用 | 重要性 | 备注 |
---|---|---|---|
unsafeWindow | 运行页面的window | 1 | |
Subresource Integrity | 外部资源完整性检查(hash) | ||
GM_addElement(tag_name, attributes), GM_addElement(parent_node, tag_name, attributes) | 添加html元素 | 1 | |
GM_addStyle(css) | 添加css | 1 | |
GM_download(details), GM_download(url, name) | 文件下载 | 1 | |
GM_getResourceText(name) | 获取由 @resource 预加载的(文本)资源 | 如脚本,css | |
GM_getResourceURL(name) | 读取header @resource中特定资源的链接 | 例如图片的url | |
GM_info | 打印脚本的详情 | ||
GM_log(message) | 打印log | ||
GM_notification(details, ondone), GM_notification(text, title, image, onclick) | 右下角弹窗提示 | ||
GM_openInTab(url, options), GM_openInTab(url, loadInBackground) | 打开url链接 | ||
GM_registerMenuCommand(name, callback, accessKey) | 注册tampermonkey菜单功能 | 1 | |
GM_unregisterMenuCommand(menuCmdId) | 移除tampermonkey菜单功能 | 1 | |
GM_setClipboard(data, info) | 复制内容到剪切板 | 1 | |
GM_getTab(callback) | 获取选项卡对象, 生命周期为选项卡的打开->关闭 | ||
GM_saveTab(tab) | 保存选项卡对象, 生命周期为选项卡的打开->关闭 | ||
GM_getTabs(callback) | 获取所有选项卡对象, 生命周期为选项卡的打开->关闭 | ||
GM_setValue(key, value) | 设置存储值 | 1 | |
GM_getValue(key, defaultValue) | 读取存储值 | 1 | |
GM_deleteValue(key) | 删除存储值 | 1 | |
GM_listValues() | 罗列出所有的脚本存储的数据 | ||
GM_addValueChangeListener(key, (key, old_value, new_value, remote) => void) | 监听存储值的改变 | 1 | |
GM_removeValueChangeListener(listenerId) | 移除存储值监听 | 1 | |
GM_xmlhttpRequest(details) | (跨域)xhr请求 | 1 | |
GM_webRequest(rules, listener) | 监听匹配规则的url web请求拦截 | 实验性api | |
GM_cookie.list(details[, callback]) | (全部)cookie读取 | 实验性api | |
GM_cookie.set(details[, callback]) | 设置cookie | ||
GM_cookie.delete(details, callback) | 删除cookie | ||
window.onurlchange | 监听url的改变 | 1 | |
window.close | 关闭标签 | ||
window.focus | 窗体激活 |
3.1 unsafeWindow
This object allows a User script to access "custom" properties set by the web page. The user script is otherwise isolated from these properties.
USE OF UNSAFEWINDOW IS INSECURE, AND IT SHOULD BE AVOIDED WHENEVER POSSIBLE.
unsafeWindow bypasses Greasemonkey's security model, which exists to make sure that malicious web pages cannot alter objects in such a way as to make user scripts (which execute with more privileges than ordinary JavaScript running in a web page) do things that their authors or users did not intend. User scripts should therefore avoid calling or in any other way depending on any properties on unsafeWindow - especially if they are executed for arbitrary web pages, such as those with @include *.
The unsafeWindow
object provides access to the window
object of the page that Tampermonkey is running on, rather than the window
object of the Tampermonkey extension. This can be useful in some cases, such as when a userscript needs to access a JavaScript library or variable that is defined on the page.
unsafeWindow
对象提供对Tampermonkey
正在运行的页面的window
对象的访问, 而不是Tampermonkey
扩展的window
对象. 在某些情况下很有用, 例如当用户脚本需要访问页面上定义的JavaScript
库或变量时.
这个对象unsafeWindow
, 并不需要grant
就可以在tampermonkey
脚本中访问(在浏览器的console
无法访问到), 只需要启用任意的@grant ...
或者不设置@grant
均可访问到这个对象.
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// @grant unsafeWindow
// @unwrap
// ==/UserScript==
// @unwrap, 将脚本完全暴露了?
this.window.abc_test = 'test';
// this.unsafeWindow.abc_xxx = 'xxx'; // error, undefined
// unsafeWindow.abc_bbc = 'bbc'; // error, undefined
window.abc_cbs = 'cbs';
console.log(this === window); true
console.log(this.window === window); // true
console.log(this.unsafeWindow === this.window); //false
console.log(this);
console.log(window);
console.log(unsafeWindow); // error, undefined
但是@unwrap
这意味着, 用户脚本完全暴露在目标页面上(?).
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// ==/UserScript==
console.log(this);
console.log(unsafeWindow);
// tampermonkey的源码截取
{
unsafeWindow: typeof globalThis === "undefined" ? window : globalThis,
unsafeThis: window,
vault: window["lkqd4h65.tdr"],
contextId: "lkqd4h65.tdr",
__proto__: null
}
可以看到, this
指向一个Proxy
对象.
new Proxy(Window, {});
(测试这个api
时, 启用和不启用, 要**重启(务必)**一下浏览器)
由于相关的资料很少(包括Google
也没有相对清晰的解释该问题), 此文的作者这种疑惑是最为常见的, 貌似不需要unsafeWindow
也可以操作window
对象的内容. 在前面已经提过了, 脚本操作的逻辑是用户脚本可以访问(操作)目标页面脚本的内容, 但是页面脚本无法反向访问(操作)用户脚本.
(也许该文的作者也一定程度受到没用重启浏览器的影响.)
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// @grant unsafeWindow
// ==/UserScript==
// 加上 // @sandbox DOM, 以下结果还是一样?
this.window.abc_test = 'test';
this.unsafeWindow.abc_xxx = 'xxx'; // 还是只能访问到这个
unsafeWindow.abc_bbc = 'bbc'; // 还是只能访问到这个
window.abc_cbs = 'cbs';
console.log(this === window); // true
console.log(this.window === window); // true
console.log(this.unsafeWindow === this.window);
console.log(this); // Proxy
console.log(window); // Proxy
console.log(unsafeWindow); //window
综上, 可以看到在不使用// @unwrap
的情况下, 脚本this
指向是一个Window Proxy
, window
实际上是tampermonkey
的content.js
运行环境的window
. 对这个window
上的修改, 是无法直接作用于目标页面的window
上.
- javascript - Why is window (and unsafeWindow) not the same from a userscript as from a tag? - Stack Overflow.
- javascript - Are Chrome user-scripts separated from the global namespace like Greasemonkey scripts? - Stack Overflow
- 如果我想使用 unsafeWindow API, 是否需要使用" @grant unsafeWindow" ? (google.com)
3.2 外部资源引入
3.2.1 Subresource Integrity
子资源完整性( Subresource Integrity, SRI) 是允许浏览器检查其获得的资源( 例如从 CDN 获得的) 是否被篡改的一项安全特性. 它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改
Subresource Integrity (SRI) is a security feature that allows userscript developers to ensure that the external resources (such as JavaScript libraries and CSS files) that they include in their userscript have not been tampered with or modified. This is accomplished by generating a cryptographic hash of the resource and including it in @require
and @resource
tags. When the userscript is installed, Tampermonkey will calculate the hash of the resource and compare it to the included hash. If the two hashes do not match, Tampermonkey will refuse to load the resource, preventing attackers from injecting malicious code into your userscript.
The hash component of the URL of @resource
and @require
tags is used for this purpose.
// @resource SRIsecured1 http://example.com/favicon1.ico#md5=ad34bb...
// @resource SRIsecured2 http://example.com/favicon2.ico#md5=ac3434...,sha256=23fd34...
// @require https://code.jquery.com/jquery-2.1.1.min.js#md5=45eef...
// @require https://code.jquery.com/jquery-2.1.2.min.js#md5-ac56d...,sha256-6e789...
// @require https://code.jquery.com/jquery-3.6.0.min.js#sha256-/xUj+3OJU...ogEvDej/m4=
Tampermonkey supports SHA-256
and MD5
hashes natively, all other (SHA-1
, SHA-384
and SHA-512
) depend on window.crypto.
In case multiple hashes (separated by comma or semicolon) are given the last currently supported one is used by Tampermonkey. All hashes need to be encoded in either hex or Base64 format.
3.2.2 GM_getResourceText(name)
读取 @resource
预加载资源
Allows userscripts to access the text of a resource (such as a JavaScript or CSS file) that has been included in a userscript via @resource
.
The function takes a single parameter, which is the "name" of the resource to retrieve. It returns the text of the resource as a string.
Here is an example of how the function might be used:
const scriptText = GM_getResourceText("myscript.js");
const script = document.createElement("script");
script.textContent = scriptText;
document.body.appendChild(script);
3.2.3 GM_getResourceURL(name)
读取@resource
预加载资源特定资源的url
GM_getResourceURL
allows userscripts to access the URL of a resource (such as a CSS or image file) that has been included in the userscript via a @resource
tag at the script header.
The function takes a single parameter, which is the "name" of the resource to retrieve. It returns the URL of the resource as a string.
const imageUrl = GM_getResourceURL("myimage.png");
const image = document.createElement("img");
image.src = imageUrl;
document.body.appendChild(image);
3.3 元素添加
3.3.1 GM_addElement(tag_name, attributes), GM_addElement(parent_node, tag_name, attributes)
GM_addElement
allows Tampermonkey scripts to add new elements to the page that Tampermonkey is running on. This can be useful for a variety of purposes, such as adding script
and img
tags if the page limits these elements with a content security policy (CSP).
It creates an HTML element specified by "tag_name" and applies all given "attributes" and returns the injected HTML element. If a "parent_node" is given, then it is attached to it or to document head or body otherwise.
'GM_addElement
允许Tampermonkey
向正在运行的页面添加新元素. 这对于特定目的都很有用, 例如, 如果页面使用内容安全策略(CSP)限制这些元素.
它创建一个由*" tag_name" 指定的HTML元素, 并应用所有给定的" 属性" , 并返回注入的HTML元素. 如果给出了"parent_node"*, 则将其附加到它或附加到文档头部或正文.
For suitable "attributes", please consult the appropriate documentation. For example:
GM_addElement('script', {
textContent: 'window.foo = "bar";'
});
GM_addElement('script', {
src: 'https://example.com/script.js',
type: 'text/javascript'
});
GM_addElement(document.getElementsByTagName('div')[0], 'img', {
src: 'https://example.com/image.png'
});
GM_addElement(shadowDOM, 'style', {
textContent: 'div { color: black; };'
});
Note: this feature is experimental and the API may change.
3.3.2 GM_addStyle(css)
向目标html注入css元素.
Adds the given style to the document and returns the injected style element.
返回创建的css节点.
const css = GM_addStyle(css);
3.4 菜单命令
// ==UserScript==
// @name bili_speed_control
// @namespace https://github.com/Kyouichirou
// @version 0.1
// @description control bilibili video play speed!
// @author You
// @include https://www.bilibili.com/video/BV*
// @grant GM_registerMenuCommand
// @noframes
// ==/UserScript==
// 一个简单的bilibili播放速度控制
(() => {
'use strict';
let is_first = true;
let video_speed = 2;
let video = document.getElementsByTagName('video');
if (video.length === 0) {
video = document.getElementsByClassName('bwp-video');
if (video.length > 0) video = video[0];
else {
console.warn('no video element of bwp-video');
video = null;
}
} else video = video[0];
if (video) {
video.oncanplay = (e) => {
if (is_first) return;
const target = e.target;
if (target.playbackRate !== video_speed) setTimeout(() => { target.playbackRate = video_speed }, 1000);
}
const speed_control = (mode) => {
is_first = false;
video_speed += (mode ? 0.5 : -0.5);
if (video_speed < 5) {
video.playbackRate = video_speed;
is_first = video_speed < 2;
}
}
GM_registerMenuCommand(
'speedup',
speed_control.bind(null, true),
);
GM_registerMenuCommand(
'slow',
speed_control.bind(null, false),
);
console.log('speed control ready');
}
})();
3.4.1 GM_registerMenuCommand(name, callback, accessKey)
注册菜单命令
GM_registerMenuCommand
allows userscripts to add a new entry to the userscript's menu in the browser, and specify a function to be called when the menu item is selected.
The function takes three parameters:
-
name: A string containing the text to display for the menu item.
函数名称.
-
callback: A function to be called when the menu item is selected. The function will be passed a single parameter, which is the currently active tab. As of Tampermonkey 4.14 a MouseEvent or KeyboardEvent is passed as function argument.
调用的函数
-
accessKey: An optional access key for the menu item. This can be used to create a shortcut for the menu item. For example, if the access key is "s", the user can select the menu item by pressing "s" when Tampermonkey's popup-menu is open.
快捷键, 默认
s
The function return a menu entry ID that can be used to unregister the command.
函数返回一个id, 用于解除注册的函数.
Here is an example of how the function might be used:
const menu_command_id = GM_registerMenuCommand("Show Alert", function(event: MouseEvent | KeyboardEvent) {
alert("Menu item selected");
}, "a");
3.4.2 GM_unregisterMenuCommand(menuCmdId)
解除菜单的注册命令.
GM_unregisterMenuCommand
removes an existing entry from the userscript's menu in the browser.
The function takes a single parameter, which is the ID of the menu item to remove. It does not return a value.
Here is an example of how the function might be used:
const menu_command_id = GM_registerMenuCommand(...);
GM_unregisterMenuCommand(menu_command_id);
3.5 标签信息
3.5.1 GM_getTab(callback)
The GM_getTab function takes a single parameter, a callback function that will be called with an object that is persistent as long as this tab is open.
GM_getTab((tab) => console.log(tab));
3.5.2 GM_saveTab(tab)
这个api主要用于同一脚本, 不同标签之间的通信.
例如A标签设置了tab标签信息, B标签打开, 读取标签信息, 根据tab信息操作.
同时A标签关闭之后, B在执行某个动作可以先判断是否tab信息还存在, 然后再继续执行.
这个api
相当于GM_addValueChangeListener()
的弱化版本, 以及自动化版本(关闭标签则自动销毁对象),
The GM_saveTab
function allows a userscript to save information about a tab for later use.
The function takes a "tab" parameter, which is an object containing the information to be saved about the tab.
The GM_saveTab
function saves the provided tab information, so that it can be retrieved later using the GM_getValue
function.
Here is an example of how the GM_saveTab function might be used in a userscript:
GM_getTab(function(tab) {
tab.newInfo = "new!";
GM_saveTab(tab); // 两个api一起搭配使用
});
// 当tab关闭之后, tab对象将自动销毁
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.qq.com/
// @grant unsafeWindow
// @grant GM_getTab
// @grant GM_getTabs
// @grant GM_saveTab
// ==/UserScript==
(() => {
GM_getTabs((tabs) => {
if (Object.keys(tabs).length > 0) {
console.log(tabs);
} else {
GM_getTab((tab) => {
console.log(tab);
tab.id = 'test';
GM_saveTab(tab);
});
}
});
})();
In this example, the GM_saveTab
function is called with the tab object returned by the GM_getTab
function, and a new key called "newInfo".
3.5.3 GM_getTabs(callback)
The GM_getTabs
function takes a single parameter: a callback function that will be called with the information about the tabs.
The "tabs" object that is passed to the callback function contains objects, with each object representing the saved tab information stored by GM_saveTab
.
GM_getTabs((tabs) => {
for (const [tabId, tab] of Object.entries(tabs)) {
console.log(`tab ${tabId}`, tab);
}
});
3.6 脚本存储
3.6.1 GM_setValue(key, value)
设置存储的值.
The GM_setValue
allows a userscript to set the value of a specific key in the userscript's storage.
The GM_setValue
function takes two parameters:
- A string specifying the key for which the value should be set.
- The value to be set for the key. This value can be of any type (string, number, object, etc.).
The GM_setValue
function does not return any value. Instead, it sets the provided value for the specified key in the userscript's storage.
Here is an example of how GM_setValue
and its async pendant GM.setValue
might be used in a userscript:
GM_setValue("someKey", "someData");
await GM.setValue("otherKey", "otherData");
3.6.2 GM_getValue(key, defaultValue)
读取存储值
The GM_getValue
function allows a userscript to retrieve the value of a specific key in the extension's storage. It takes two parameters:
- A string specifying the key for which the value should be retrieved.
- A default value to be returned if the key does not exist in the extension's storage. This default value can be of any type (string, number, object, etc.).
The GM_getValue
function returns the value of the specified key from the extension's storage, or the default value if the key does not exist.
Here is an example of how the GM_getValue
function might be used in a userscript:
const someKey = GM_getValue("someKey", null);
// 这里需要注意, 这个api是为了兼容Greasemonkey而推出的, 优先级GM_getValue > GM.getValue
// Greasemonkey的这个api是异步执行的.
const otherKey = await GM.getValue("otherKey", null);
In this example, the GM_getValue
function is called with the key "someKey" and a default value of null. If the "someKey" key exists in the extension's storage, its value will be returned and stored in the someKey variable. If the key does not exist, the default value of null will be returned and stored in the savedTab variable.
- GM.getValue - GreaseSpot Wiki
- (need help) idk why GM_getValue became async - Issue #579 - Tampermonkey/tampermonkey - GitHub
- Question about async/await? - Issue #2755 - greasemonkey/greasemonkey - GitHub
3.6.3 GM_deleteValue(key)
删除存储值.
Deletes "key" from the userscript's storage.
GM_deleteValue("someKey");
await GM.deleteValue("otherKey");
3.6.4 GM_listValues()
列出所有的存储值.
The GM_listValues
function returns a list of keys of all stored data.
const keys = GM_listValues();
const asyncKeys = await GM.listValues();
3.6.5 GM_addValueChangeListener(key, (key, old_value, new_value, remote) => void)
监听存储值的变化, 可以用于多标签之间的脚本通信.
The GM_addValueChangeListener
function allows a userscript to add a listener for changes to the value of a specific key in the userscript's storage.
The function takes two parameters:
-
A string specifying the key for which changes should be monitored.
-
A callback function that will be called when the value of the key changes. The callback function should have the following signature:
function(key, oldValue, newValue, remote) { // key is the key whose value has changed, 键值 // oldValue is the previous value of the key, 旧值 // newValue is the new value of the key, 新值 // remote is a boolean indicating whether the change originated from a different userscript instance // 改变的来源, 是否为同一脚本的实例 }
The GM_addValueChangeListener
function returns a "listenerId" value that can be used to remove the listener later using the GM_removeValueChangeListener
function. The very same applies to GM.addValueChangeListener
and GM.removeValueChangeListener
with the only difference that both return a promise;
Here is an example of how the GM_addValueChangeListener
function might be used in a userscript:
// Add a listener for changes to the "savedTab" key
var listenerId = GM_addValueChangeListener("savedTab", function(key, oldValue, newValue, remote) {
// Print a message to the console when the value of the "savedTab" key changes
console.log("The value of the '" + key + "' key has changed from '" + oldValue + "' to '" + newValue + "'");
});
GM_addValueChangeListener
can be used by userscripts to communicate with other userscript instances at other tabs.
需要注意这个监听并不是写入值就会触发回调函数, 而是值的内容必须发生变化才会触发(不管是对象还是字符串, 数字), 首次无值就一定会触发.
// ==UserScript==
// @name test
// @namespace http://tampermonkey.net/
// @description try to take over the world!
// @author You
// @match https://www.ithome.com/*
// @grant GM_setValue
// @grant GM_addValueChangeListener
// @noframes
// ==/UserScript==
(function() {
'use strict';
// Your code here...
GM_addValueChangeListener('test', () => console.log('a')); // 只能触发一次
document.onclick = () => GM_setValue('test', 1);
})();
3.6.6 GM_removeValueChangeListener(listenerId)
移除脚本监听
GM_removeValueChangeListener
and GM.removeValueChangeListener
both get one argument called "listenerId" and remove the change listener with this ID.
3.7 网络请求
3.7.1 GM_xmlhttpRequest(details)
The GM_xmlhttpRequest
allows a userscripts to send an HTTP request and handle the response. The function takes a single parameter: an object containing the details of the request to be sent and the callback functions to be called when the response is received.
The object can have the following properties:
-
method one of GET, HEAD, POST
支持三种模式
-
url the destination URL
访问的url
-
headers e.g.
user-agent
,referer
, ... (some special headers are not supported by Safari and Android browsers)http表头
-
data some string to send via a POST request
使用post时, 需要提交的数据
-
redirect one of
follow
,error
ormanual
; controls what to happen when a redirect is detected (build 6180+, enforcesfetch
mode) -
cookie a cookie to be patched into the sent cookie set
需要发送的cookie
-
binary send the data string in binary mode
以二进制模式发送数据
-
nocache don't cache the resource
不要缓存内容
-
revalidate revalidate maybe cached content
重新验证可能缓存的内容
-
timeout a timeout in ms
超时的事件
-
context a property which will be added to the response object
向响应内容添加一个属性
-
responseType one of
arraybuffer
,blob
,json
orstream
返回的响应内容的格式
-
overrideMimeType a MIME type for the request
请求的MIME类型
-
anonymous don't send cookies with the request (enforces
fetch
mode)匿名模式, 不发生cookie
-
fetch use a
fetch
instead of aXMLHttpRequest
request (at Chrome this causesdetails.timeout
andxhr.onprogress
to not work and makesxhr.onreadystatechange
receive onlyreadyState
DONE
(==4) events)使用fetch替代XMLHttpRequest, 注意差异
-
user a user name for authentication
授权用户
-
password a password
授权密码
-
onabort callback to be executed if the request was aborted
放弃请求回调
-
onerror callback to be executed if the request ended up with an error
错误回调
-
onloadstart callback to be executed on load start, provides access to the stream object if responseType is set to
stream
请求开始后回调
-
onprogress callback to be executed if the request made some progress
在请求中回调
-
onreadystatechange callback to be executed if the request's
readyState
changed请求状态发生改变回调
-
ontimeout callback to be executed if the request failed due to a timeout
超时回调
-
onload
请求完成回调
callback to be executed if the request was loaded.
function(response) { // response is an object containing the details of the response }
response
has the following attributes:
- finalUrl - the final URL after all redirects from where the data was loaded
- readyState - the request's
readyState
- status - the request's status
- statusText - the request's status text
- responseHeaders - the request's response headers
- response - the response data as object if
details.responseType
was set - responseXML - the response data as XML document
- responseText - the response data as plain string
GM_xmlhttpRequest
returns an object with the following property:
该函数返回一个对象, 具有放弃方法.
- abort - function to be called to cancel this request
Here is an example of how the GM_xmlhttpRequest
function might be used in a userscript:
GM_xmlhttpRequest({
method: "GET",
url: "https://example.com/",
headers: {
"Content-Type": "application/json"
},
onload: function(response) {
console.log(response.responseText);
}
});
Note: the synchronous
flag at details
is not supported
注意: 同步模式在详情中不被支持
Important: if you want to use this method then please also check the documentation about @connect
.
需要设置 @connect example.com
.
3.7.2 GM_download(details), GM_download(url, name)
下载文件.
GM_download
allows userscripts to download a file from a specified URL and save it to the user's local machine.
The GM_download
function takes the following parameters:
details can have the following attributes:
-
url: The URL of the file to download. This must be a valid URL and must point to a file that is accessible to the user.
下载链接
-
name: The name to use for the downloaded file. This should include the file's extension, such as .txt or .pdf. For security reasons the file extension needs to be whitelisted at Tampermonkey's options page.
文件名
-
headers: An object containing HTTP headers to include in the download request. See
GM_xmlhttpRequest
for more details.http请求头
-
saveAs: A boolean value indicating whether to use the user's default download location, or to prompt the user to choose a different location. This option works in browser API mode only.
是否出现
savaAs
弹窗提示 -
conflictAction: A string that control what happens when a file with this name already exists. This option works in browser API mode only. Possible values are
uniquify
,overwrite
andprompt
. Please check this link for more details.冲突之后的处理
-
onload: A function to call when the download has completed successfully.
下载完成回调函数
-
onerror: A function to call if the download fails or is cancelled.
错误回调函数
-
onprogress A callback to be executed if this download made some progress.
在下载中回调函数
-
ontimeout A callback to be executed if this download failed due to a timeout.
超时回调函数
The download argument of the onerror callback can have the following attributes:
-
error
错误对象属性
: error reason
- not_enabled - the download feature isn't enabled by the user
- not_whitelisted - the requested file extension is not whitelisted
- not_permitted - the user enabled the download feature, but did not give the downloads permission
- not_supported - the download feature isn't supported by the browser/version
- not_succeeded - the download wasn't started or failed, the details attribute may provide more information
-
details: detail about that error
Returns an object with the following property:
-
abort: A function which can be called to cancel this download.
放弃下载
Depending on the download mode GM_info
provides a property called downloadMode
which is set to one of the following values: native, disabled or browser.
GM_download("http://example.com/file.txt", "file.txt");
const download = GM_download({
url: "http://example.com/file.txt",
name: "file.txt",
saveAs: true
});
// cancel download after 5 seconds
window.setTimeout(() => download.abort(), 5000);
Note: The browser might modify the desired filename. Especially a file extension might be added if the browser finds this to be safe to download at the current OS.
注意下载的文件格式, 出于浏览器安全原因.
3.7.3 GM_webRequest(rules, listener)
Note: this API is experimental and might change at any time. It might also disappear or change during manifest v3 migration.
GM_webRequest
(re-)registers rules for web request manipulations and the listener of triggered rules. If you need to just register rules it's better to use @webRequest
header. Note, webRequest proceeds only requests with types sub_frame
, script
, xhr
and websocket
.
对于子框架, 脚本, xhr
, websocket
的网络请求进行拦截, 如重定向.
Parameters:
- rules, object[], array of rules with following properties:
-
selector - string|object, for which URLs the rule should be triggered, string value is shortening for
{ include: [selector] }
, object properties: -
include - string|string[], URLs, patterns, and regexpes for rule triggering;
- match - string|string[], URLs and patterns for rule trigering;
-
exclude - string|string[], URLs, patterns, and regexpes for not triggering the rule;
-
action, string | object, what to do with the request, string value
"cancel"
is shortening for{ cancel: true }
, object properties:- cancel - boolean, whether to cancel the request;
- redirect - string|object, redirect to some URL which must be included in any @match or @include header. When a string, redirects to the static URL. If object:
- from - string, a regexp to extract some parts of the URL, e.g.
"([^:]+)://match.me/(.*)"
; - to - string, pattern for substitution, e.g.
"$1://redirected.to/$2"
;
- from - string, a regexp to extract some parts of the URL, e.g.
-
- listener - function, is called when the rule is triggered, cannot impact on the rule action, arguments:
-
info - string, type of action:
"cancel"
,"redirect"
; -
message - string,
"ok"
or"error"
; -
details - object, info about the request and rule:
- rule - object, the triggered rule;
-
url - string, URL of the request;
- redirect_url - string, where the request was redirected;
-
description - string, error description.
-
Example
GM_webRequest([
{ selector: '*cancel.me/*', action: 'cancel' },
{ selector: { include: '*', exclude: 'http://exclude.me/*' }, action: { redirect: 'http://new_static.url' } },
{ selector: { match: '*://match.me/*' }, action: { redirect: { from: '([^:]+)://match.me/(.*)', to: '$1://redirected.to/$2' } } }
], function(info, message, details) {
console.log(info, message, details);
});
3.8 cookie
cookie
的构成:
-
name
-
value
-
domain
Domain
和Path
标识定义了 Cookie 的作用域: 即允许 Cookie 应该发送给哪些 URL.Domain
指定了哪些主机可以接受 Cookie. 如果不指定, 该属性默认为同一 host 设置 cookie, 不包含子域名. 如果指定了Domain
, 则一般包含子域名. 因此, 指定Domain
比省略它的限制要少. 但是, 当子域需要共享有关用户的信息时, 这可能会有所帮助.例如, 如果设置
Domain=mozilla.org
, 则 Cookie 也包含在子域名中( 如developer.mozilla.org
) . -
path
Path
属性指定了一个 URL 路径, 该 URL 路径必须存在于请求的 URL中, 以便发送Cookie
标头. 以字符%x2F
(" /" ) 作为路径分隔符, 并且子路径也会被匹配.例如, 设置
Path=/docs
, 则以下地址都会匹配:/docs
/docs/
/docs/Web/
/docs/Web/HTTP
但是这些请求路径不会匹配以下地址:
/
/docsets
/fr/docs
-
expires, 和
localstorage
不同,cookie
是有使用寿命的. -
httponly
有两种方法可以确保
Cookie
被安全发送, 并且不会被意外的参与者或脚本访问:Secure
属性和HttpOnly
属性.标记为
Secure
的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端. 它永远不会使用不安全的 HTTP 发送( 本地主机除外) , 这意味着中间人攻击者无法轻松访问它. 不安全的站点( 在 URL 中带有http:
) 无法使用Secure
属性设置 cookie. 但是,Secure
不会阻止对 cookie 中敏感信息的访问. 例如, 有权访问客户端硬盘( 或, 如果未设置HttpOnly
属性, 则为 JavaScript) 的人可以读取和修改它.JavaScript
Document.cookie
API 无法访问带有HttpOnly
属性的 cookie; 此类 Cookie 仅作用于服务器. 例如, 持久化服务器端会话的 Cookie 不需要对 JavaScript 可用, 而应具有HttpOnly
属性. 此预防措施有助于缓解跨站点脚本( XSS) (en-US)攻击. -
secure
-
samesite
SameSite
属性允许服务器指定是否/何时通过跨站点请求发送( 其中站点由注册的域和方案定义: http 或 https) . 这提供了一些针对跨站点请求伪造攻击( CSRF) 的保护. 它采用三个可能的值:Strict
,Lax
和None
.使用
Strict
, cookie 仅发送到它来源的站点.Lax
与 Strict 相似, 只是在用户导航到 cookie 的源站点时发送 cookie. 例如, 通过跟踪来自外部站点的链接.None
指定浏览器会在同站请求和跨站请求下继续发送 cookie, 但仅在安全的上下文中( 即, 如果SameSite=None
, 且还必须设置Secure
属性) . 如果没有设置SameSite
属性, 则将 cookie 视为Lax
.Set-Cookie: mykey=myvalue; SameSite=Strict
3.8.1 GM_cookie.list(details[, callback])
Note: the GM_cookie API is experimental and might return a not supported
error at some Tampermonkey versions.
Tampermonkey checks if the script has @include
or @match
access to given details.url
arguments!
Parameters:
-
details
object, containing properties of the cookies to retrieve
- url string?, representing the URL to retrieve cookies from (defaults to current document URL)
- domain string?, representing the domain of the cookies to retrieve
- name string?, representing the name of the cookies to retrieve
- path string?, representing the path of the cookies to retrieve
-
callback
function, to be called when the cookies have been retrieved. The function will be passed two arguments:
- cookies object[], representing the retrieved cookies
-
error string, representing an error message if an error occurred, null otherwise.
The cookie objects have the following properties:
- domain string, representing the domain of the cookie
- firstPartyDomain string?: the first party domain of the cookie.
- hostOnly boolean, indicating whether the cookie is a host-only cookie
- httpOnly boolean, indicating whether the cookie is an HTTP-only cookie
- name string, representing the name of the cookie
- path string, representing the path of the cookie
- sameSite string, indicating the SameSite attribute of the cookie
- secure boolean, indicating whether the cookie requires a secure connection
- session boolean, indicating whether the cookie is a session cookie
- value string, representing the value of the cookie
Example usage:
// Retrieve all cookies with name "mycookie"
GM_cookie.list({ name: "mycookie" }, function(cookies, error) {
if (!error) {
console.log(cookies);
} else {
console.error(error);
}
});
// Retrieve all cookies for the current domain
const cookies = await GM.cookies.list()
console.log(cookies);
3.8.2 GM_cookie.set(details[, callback])
设置cookie.
Sets a cookie with the given details. Supported properties are defined here.
Parameters:
-
details
: An object containing the details of the cookie to be set. The object can have the following properties:
- url string?, the URL to associate the cookie with. If not specified, the cookie is associated with the current document's URL.
- name string, the name of the cookie.
- value string, the value of the cookie.
- domain string?, the domain of the cookie.
- firstPartyDomain string?: the first party domain of the cookie.
- path string?, the path of the cookie.
- secure boolean?, whether the cookie should only be sent over HTTPS.
- httpOnly boolean?, whether the cookie should be marked as HttpOnly.
- expirationDate number?, the expiration date of the cookie in seconds since the Unix epoch. If not specified, the cookie never expires.
-
callback
function
, a function to be called when the operation is complete. The function is passed one argument:
- error string?, if there was an error setting the cookie, this contains an error message. Otherwise, it is
undefined
.
- error string?, if there was an error setting the cookie, this contains an error message. Otherwise, it is
Example:
GM_cookie.set({
url: 'https://example.com',
name: 'name',
value: 'value',
domain: '.example.com',
path: '/',
secure: true,
httpOnly: true,
expirationDate: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 30) // Expires in 30 days
}, function(error) {
if (error) {
console.error(error);
} else {
console.log('Cookie set successfully.');
}
});
GM.cookie.set({
name: 'name',
value: 'value'
})
.then(() => {
console.log('Cookie set successfully.');
})
.catch((error) => {
console.error(error);
});
3.8.3 GM_cookie.delete(details, callback)
删除一个cookie.
Deletes a cookie.
Parameters:
The details
object must contain at least one of the following properties:
- url string?, the URL associated with the cookie. If
url
is not specified, the current document's URL will be used. - name string?, the name of the cookie to delete.
- firstPartyDomain string?: the first party domain of the cookie to delete.
The callback
function is optional and will be called when the cookie has been deleted or an error has occurred. It takes one argument:
- error string?, an error message, or
undefined
if the cookie was deleted successfully.
Example:
GM_cookie.delete({ name: 'cookie_name' }, function(error) {
if (error) {
console.error(error);
} else {
console.log('Cookie deleted successfully');
}
});
3.9 其他
3.9.1 window.onurlchange
监听页面url
的变化, 这对于越来越多采用异步框架单一页面的站点尤为重要.
If a script runs on a single-page application, then it can use window.onurlchange
to listen for URL changes:
// ==UserScript==
...
// @grant window.onurlchange
// ==/UserScript==
if (window.onurlchange === null) {
// feature is supported
window.addEventListener('urlchange', (info) => ...);
}
// ==UserScript==
// @name music163
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://music.163.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=163.com
// @grant window.onurlchange
// ==/UserScript==
(function() {
'use strict';
// Your code here...
if (window.onurlchange === null) {
console.log('support');
window.addEventListener('urlchange', (info) => console.log(info)); // return {url: '....'}
}
})();
3.9.2 window.close
关闭标签页, 一般情况下, JavaScript
的window.close()
只能关闭由其自身打开的标签.
Usually JavaScript is not allowed to close tabs via window.close
. Userscripts, however, can do this if the permission is requested via @grant
.
Note: for security reasons it is not allowed to close the last tab of a window.
注意: 无法关闭浏览器剩下的最后一个标签, 出于安全原因.
// ==UserScript==
...
// @grant window.close
// ==/UserScript==
if (condition) {
window.close();
}
3.9.3 window.focus
标签窗体激活.
window.focus
brings the window to the front, while unsafeWindow.focus
may fail due to user settings.
// ==UserScript==
...
// @grant window.focus
// ==/UserScript==
if (condition) {
window.focus();
}
3.9.4 GM_setClipboard(data, info)
将内容复制到剪切板.
GM_setClipboard
sets the text of the clipboard to a specified value.
The function takes a parameter "data", which is the string to set as the clipboard text and a parameter "info".
"info" can be just a string expressing the type text
or html
or an object like
info
不限于text文本, object, html一样可以.
{
type: 'text',
mimetype: 'text/plain'
}
GM_setClipboard("This is the clipboard text.", "text");
3.9.5 GM_info
脚本详情.
Get some info about the script and TM. The object might look like this:
type ScriptGetInfo = {
downloadMode: string,
isFirstPartyIsolation?: boolean,
isIncognito: boolean,
sandboxMode: SandboxMode,
scriptHandler: string,
scriptMetaStr: string | null,
scriptUpdateURL: string | null,
scriptWillUpdate: boolean,
version?: string,
script: {
antifeatures: { [antifeature: string]: { [locale: string]: string } },
author: string | null,
blockers: string[],
connects: string[],
copyright: string | null,
deleted?: number | undefined,
description_i18n: { [locale: string]: string } | null,
description: string,
downloadURL: string | null,
excludes: string[],
fileURL: string | null,
grant: string[],
header: string | null,
homepage: string | null,
icon: string | null,
icon64: string | null,
includes: string[],
lastModified: number,
matches: string[],
name_i18n: { [locale: string]: string } | null,
name: string,
namespace: string | null,
position: number,
resources: Resource[],
supportURL: string | null,
system?: boolean | undefined,
'run-at': string | null,
unwrap: boolean | null,
updateURL: string | null,
version: string,
webRequest: WebRequestRule[] | null,
options: {
check_for_updates: boolean,
comment: string | null,
compatopts_for_requires: boolean,
compat_wrappedjsobject: boolean,
compat_metadata: boolean,
compat_foreach: boolean,
compat_powerful_this: boolean | null,
sandbox: string | null,
noframes: boolean | null,
unwrap: boolean | null,
run_at: string | null,
tab_types: string | null,
override: {
use_includes: string[],
orig_includes: string[],
merge_includes: boolean,
use_matches: string[],
orig_matches: string[],
merge_matches: boolean,
use_excludes: string[],
orig_excludes: string[],
merge_excludes: boolean,
use_connects: string[],
orig_connects: string[],
merge_connects: boolean,
use_blockers: string[],
orig_run_at: string | null,
orig_noframes: boolean | null
}
}
}
};
type SandboxMode = 'js' | 'raw' | 'dom';
type Resource = {
name: string,
url: string,
error?: string,
content?: string,
meta?: string
};
type WebRequestRule = {
selector: { include?: string | string[], match?: string | string[], exclude?: string | string[] } | string,
action: string | {
cancel?: boolean,
redirect?: {
url: string,
from?: string,
to?: string
} | string
}
};
3.9.6 GM_log(message)
打印.
Log a message to the console.
3.9.7 GM_notification(details, ondone), GM_notification(text, title, image, onclick)
右下角的弹窗提示.
GM_notification
allows users to display notifications on the screen, using a provided message and other optional parameters.
The function takes several parameters. Either a details object or multiple parameters.
The details object can have the following attributes, from which some can also be used as direct parameter.
The available options include:
-
text: A string containing the message to display in the notification.
提示内容
-
title: The title of the notification.
弹窗标题
-
image: The URL of an image to display in the notification.
弹窗展示的图片
-
highlight: A boolean flag whether to highlight the tab that sends the notfication (required unless text is set)
弹窗时是否高亮所在的
tab
. -
silent: A boolean flag whether to not play a sound
弹窗时是否发出声音
-
timeout: The time, in milliseconds, after which the notification should automatically close.
自动关闭的事件间隔
-
onclick: A callback function that will be called when the user clicks on the notification.
点击时执行的函数.
-
ondone A callback function that will be called when the notification is closed (no matter if this was triggered by a timeout or a click) or the tab was highlighted
当执行完毕后的回调函数, 不管是timeout还是手动关闭弹窗均触发.
The function does not return a value.
函数没有任何的返回值.
Here is an example of how the function might be used:
GM_notification({
text: "This is the notification message.",
title: "Notification Title",
onclick: () => alert('I was clicked!')
});
3.9.8 GM_openInTab(url, options), GM_openInTab(url, loadInBackground)
打开新的标签(tab)
GM_openInTab
allows userscripts to open a new tab in the browser and navigate to a specified URL.
The function takes two parameters:
A string names "url" containing the URL of the page to open in the new tab.
An optional options object that can be used to customize the behavior of the new tab. The available options include:
-
active: A boolean value indicating whether the new tab should be active (selected) or not. The default is false.
打开后是否激活新的
tab
-
insert: An integer indicating the position at which the new tab should be inserted in the tab strip. The default is false, which means the new tab will be added to the end of the tab strip.
打开标签的位置.
-
setParent: A boolean value indicating whether the new tab should be considered a child of the current tab. The default is false.
是否将当前的窗口设置为新的tab的父窗体.
-
incognito A boolean value that makes the tab being opened inside a incognito mode/private mode window.
在无痕模式下打开
-
loadInBackground A boolean value has the opposite meaning of active and was added to achieve Greasemonkey 3.x compatibility.
是否后台加载
The function returns an object with the function close, the listener onclose and a flag called closed.
函数返回一个对象, 具有close
方法, 方法有回调函数onclose
.
Here is an example of how the function might be used:
// Open a new tab and navigate to the specified URL
GM_openInTab("https://www.example.com/");
3.9.9 <><![CDATA[...]]></>
CDATA-based way of storing meta data is supported via compatibility option. Tampermonkey tries to automatically detect whether a script needs this option to be enabled.
var inline_src = (<><![CDATA[
console.log('Hello World!');
]]></>).toString();
eval(inline_src);
四. 小结
由于TamperMonkey
并不是(纯)商业化或者大型开源产品, 在文档, 在产品稳定性上都有一定瑕疵, 例如window.onurlchange
这个api
在刚上线时, 在不同版本的浏览器上运行差异很大, 文档细节不够等. 瑕不掩瑜, Tampermoneky应当是最佳的脚本管理器, 但是 V3
协议始终是朵乌云.
附录, chrome store
用户量最大的部分Top
扩展
Extension | Category | Installs |
---|---|---|
Google Translate | Productivity | 10M+ |
Netflix Party | Fun | 10M+ |
Tampermonkey | Productivity | 10M+ |
AdBlock — best ad blocker | Productivity | 10M+ |
Adblock Plus - free ad blocker | Productivity | 10M+ |
uBlock Origin | Productivity | 10M+ |
Adobe Acrobat | Productivity | 10M+ |
Chrome Remote Desktop | Productivity | 10M+ |
Pinterest Save button | Productivity | 10M+ |
LastPass: Free Password Manager | Productivity | 10M+ |
Avast SafePrice | Comparison, deals, coupons | Shopping | 10M+ |
Avast Online Security | Social & Communication | 10M+ |
Skype | Social & Communication | 10M+ |
Cisco Webex Extension | Social & Communication | 10M+ |
Grammarly for Chrome | Productivity | 10M+ |
Honey | Shopping | 10M+ |
AVG SafePrice | Comparison, deals, coupons | Shopping | 9M+ |
Screencastify - Screen Video Recorder | Productivity | 8M+ |
Hola Free VPN, unblock any site! | Productivity | 8M+ |