chrome.webRequest.onBeforeRequest 行为不可预测
chrome.webRequest.onBeforeRequest acting unpredictably
我正在尝试制作一个网络过滤 chrome 扩展程序,它将阻止某些网站并将其 html 替换为扩展程序中包含的阻止页面。
let bans = ["*://*.amazon.com/*", "*://*.youtube.com/*", "*://*.netflix.com/*","*://*.facebook.com/*", "*://*.twitter.com/*"];
chrome.webRequest.onBeforeRequest.addListener(
function(details){
try{
chrome.tabs.executeScript(null, {file: "content.js"});
}
catch(error){
console.error(error);
}
return {cancel: true};
},
{urls: bans},
["blocking"]
);
这应该意味着如果我尝试访问该禁止列表中的任何网站,内容脚本应该用我自己的阻止页面替换该页面。然而,出于某种原因,有些网站从未加载阻止页面,其他网站根本没有被阻止,有些网站似乎运行良好。更奇怪的是,听众似乎在根本未列在禁令数组中的网站上被触发。我无法找出这些行为之间的任何类型。
我不认为它们是问题的根源,但这是我的清单(清单 v2)中的权限
"web_accessible_resources": [
"certBlockPage.html",
"blockPageLight.html"
],
"incognito": "split",
"permissions": [
"webNavigation",
"webRequest",
"webRequestBlocking",
"tabs",
"windows",
"identity",
"http://*/*",
"https://*/*",
"<all_urls>"
]
这是 content.js 文件
window.onload = function(){
fetch(chrome.runtime.getURL('certBlockPage.html'))
.then(r => r.text())
.then(html => {
document.open();
document.write(html);
document.close;
});
}
有几个问题。
- 您没有在 executeScript 的选项中指定
runAt: 'document_start'
,因此它会等待,以防您在该选项卡中的旧页面仍在加载时导航到被禁止的站点。
- executeScript 是异步的,所以它可以很容易地 运行 在
load
事件已经在选项卡中触发之后,所以你的 window.onload 永远不会 运行。不要使用onload,直接运行代码。
- 您总是 运行 在当前聚焦的选项卡中执行脚本(术语是 活动)但该选项卡可能未聚焦(不活动).您需要使用
details.tabId
和 details.frameId
.
- 用户可能打开了一个新标签并键入了被阻止的 url 或单击了它的 link,这被您的
{cancel: true}
阻止了,但是 executeScript 将失败,因为新标签当前显示在此选项卡中的页面不能 运行 内容脚本。对于任何其他chrome://或chrome -Extension:// tab或当网络错误显示在选项卡中时。
- 如果您在不删除之前的注册的情况下再次调用 onBeforeRequest.addListener,两者都将处于活动状态。
- document.write 将在具有严格 CSP
的站点上失败
解决方案 1:webRequest + executeScript
后台脚本:
updateListener(['amazon.com', 'youtube.com', 'netflix.com']);
function updateListener(hosts) {
chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequest);
chrome.webRequest.onBeforeRequest.addListener(
onBeforeRequest, {
types: ['main_frame', 'sub_frame'],
urls: hosts.map(h => `*://*.${h}/*`),
}, [
'blocking',
]);
}
function onBeforeRequest(details) {
const {tabId, frameId} = details;
chrome.tabs.executeScript(tabId, {
file: 'content.js',
runAt: 'document_start',
matchAboutBlank: true,
frameId,
}, () => chrome.runtime.lastError && redirectTab(tabId));
// Cancel the navigation without showing that it was canceled
return {redirectUrl: 'javascript:void 0'};
}
function redirectTab(tabId) {
chrome.tabs.update(tabId, {url: 'certBlockPage.html'});
}
content.js:
fetch(chrome.runtime.getURL('certBlockPage.html'))
.then(r => r.text())
.then(html => {
try {
document.open();
document.write(html);
document.close();
} catch (e) {
location.href = chrome.runtime.getURL('certBlockPage.html');
}
});
解决方案 2:webRequest + 重定向
不需要内容脚本。
const bannedHosts = ['amazon.com', 'youtube.com', 'netflix.com'];
chrome.webRequest.onBeforeRequest.addListener(
details => ({
redirectUrl: chrome.runtime.getURL('certBlockPage.html'),
}), {
types: ['main_frame', 'sub_frame'],
urls: bannedHosts.map(h => `*://*.${h}/*`),
}, [
'blocking',
]);
解决方案 3:declarativeNetRequest + 重定向
这是最快的方法,但仅限于一组非常简单的预定义操作。请参阅文档,不要忘记在 manifest.json 中将 "declarativeNetRequest"
添加到 "permissions"
,并将 "<all_urls>"
添加到 host_permissions
(ManifestV3 仍然不支持可选权限) .
const bannedHosts = ['amazon.com', 'youtube.com', 'netflix.com'];
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: bannedHosts.map((h, i) => i + 1),
addRules: bannedHosts.map((h, i) => ({
id: i + 1,
action: {type: 'redirect', redirect: {extensionPath: '/certBlockPage.html'}},
condition: {urlFilter: `||${h}/`, resourceTypes: ['main_frame', 'sub_frame']},
})),
});
我正在尝试制作一个网络过滤 chrome 扩展程序,它将阻止某些网站并将其 html 替换为扩展程序中包含的阻止页面。
let bans = ["*://*.amazon.com/*", "*://*.youtube.com/*", "*://*.netflix.com/*","*://*.facebook.com/*", "*://*.twitter.com/*"];
chrome.webRequest.onBeforeRequest.addListener(
function(details){
try{
chrome.tabs.executeScript(null, {file: "content.js"});
}
catch(error){
console.error(error);
}
return {cancel: true};
},
{urls: bans},
["blocking"]
);
这应该意味着如果我尝试访问该禁止列表中的任何网站,内容脚本应该用我自己的阻止页面替换该页面。然而,出于某种原因,有些网站从未加载阻止页面,其他网站根本没有被阻止,有些网站似乎运行良好。更奇怪的是,听众似乎在根本未列在禁令数组中的网站上被触发。我无法找出这些行为之间的任何类型。
我不认为它们是问题的根源,但这是我的清单(清单 v2)中的权限
"web_accessible_resources": [
"certBlockPage.html",
"blockPageLight.html"
],
"incognito": "split",
"permissions": [
"webNavigation",
"webRequest",
"webRequestBlocking",
"tabs",
"windows",
"identity",
"http://*/*",
"https://*/*",
"<all_urls>"
]
这是 content.js 文件
window.onload = function(){
fetch(chrome.runtime.getURL('certBlockPage.html'))
.then(r => r.text())
.then(html => {
document.open();
document.write(html);
document.close;
});
}
有几个问题。
- 您没有在 executeScript 的选项中指定
runAt: 'document_start'
,因此它会等待,以防您在该选项卡中的旧页面仍在加载时导航到被禁止的站点。 - executeScript 是异步的,所以它可以很容易地 运行 在
load
事件已经在选项卡中触发之后,所以你的 window.onload 永远不会 运行。不要使用onload,直接运行代码。 - 您总是 运行 在当前聚焦的选项卡中执行脚本(术语是 活动)但该选项卡可能未聚焦(不活动).您需要使用
details.tabId
和details.frameId
. - 用户可能打开了一个新标签并键入了被阻止的 url 或单击了它的 link,这被您的
{cancel: true}
阻止了,但是 executeScript 将失败,因为新标签当前显示在此选项卡中的页面不能 运行 内容脚本。对于任何其他chrome://或chrome -Extension:// tab或当网络错误显示在选项卡中时。 - 如果您在不删除之前的注册的情况下再次调用 onBeforeRequest.addListener,两者都将处于活动状态。
- document.write 将在具有严格 CSP 的站点上失败
解决方案 1:webRequest + executeScript
后台脚本:
updateListener(['amazon.com', 'youtube.com', 'netflix.com']);
function updateListener(hosts) {
chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequest);
chrome.webRequest.onBeforeRequest.addListener(
onBeforeRequest, {
types: ['main_frame', 'sub_frame'],
urls: hosts.map(h => `*://*.${h}/*`),
}, [
'blocking',
]);
}
function onBeforeRequest(details) {
const {tabId, frameId} = details;
chrome.tabs.executeScript(tabId, {
file: 'content.js',
runAt: 'document_start',
matchAboutBlank: true,
frameId,
}, () => chrome.runtime.lastError && redirectTab(tabId));
// Cancel the navigation without showing that it was canceled
return {redirectUrl: 'javascript:void 0'};
}
function redirectTab(tabId) {
chrome.tabs.update(tabId, {url: 'certBlockPage.html'});
}
content.js:
fetch(chrome.runtime.getURL('certBlockPage.html'))
.then(r => r.text())
.then(html => {
try {
document.open();
document.write(html);
document.close();
} catch (e) {
location.href = chrome.runtime.getURL('certBlockPage.html');
}
});
解决方案 2:webRequest + 重定向
不需要内容脚本。
const bannedHosts = ['amazon.com', 'youtube.com', 'netflix.com'];
chrome.webRequest.onBeforeRequest.addListener(
details => ({
redirectUrl: chrome.runtime.getURL('certBlockPage.html'),
}), {
types: ['main_frame', 'sub_frame'],
urls: bannedHosts.map(h => `*://*.${h}/*`),
}, [
'blocking',
]);
解决方案 3:declarativeNetRequest + 重定向
这是最快的方法,但仅限于一组非常简单的预定义操作。请参阅文档,不要忘记在 manifest.json 中将 "declarativeNetRequest"
添加到 "permissions"
,并将 "<all_urls>"
添加到 host_permissions
(ManifestV3 仍然不支持可选权限) .
const bannedHosts = ['amazon.com', 'youtube.com', 'netflix.com'];
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: bannedHosts.map((h, i) => i + 1),
addRules: bannedHosts.map((h, i) => ({
id: i + 1,
action: {type: 'redirect', redirect: {extensionPath: '/certBlockPage.html'}},
condition: {urlFilter: `||${h}/`, resourceTypes: ['main_frame', 'sub_frame']},
})),
});