我正在编写一个Chrome扩展,它将根据来自另一个网页的内容(在另一个域上)向页面添加一些信息。
这是通过要求后台服务工作人员加载页面,然后将相关信息传递回内容脚本来完成的。
重要的是页面被加载,就像用户打开一个带有该页面的选项卡一样,所以它将使用用户的登录和其他上下文(locale等)。
content.js
chrome.runtime.sendMessage('some-query', function(response) {
console.log(response)
})
background.js
chrome.runtime.onMessage.addListener((message, sender, reply) => {
loadInBackground(message, reply)
return true
})
function loadInBackground(query, reply) {
console.log('searching for', query)
let url = new URL("https://some.site.com/search")
url.searchParams.append("query", query)
// How to load the page?
reply('data-from-loaded-page')
}
我尝试过使用fetch
,但这会产生CORS错误,即使没有,我也不希望它像加载在选项卡中一样使用状态。那么,我能用什么隐藏的Chrome来完成这个任务呢?我看过所有这些,但找不到我需要的API。
发布于 2022-02-12 16:05:50
若要在扩展脚本中使用fetch
,请将该URL或"<all_urls>"
添加到主机权限中。
但是还有一个更大的问题:站点的脚本不能使用fetch
运行,因此在许多动态构造自己的现代页面上提取没有什么有用的。
在这种情况下,扩展的唯一解决办法是在一个新的选项卡或iframe中打开该站点,因为Chrome扩展不像Chrome那样具有WebView,因此扩展不能完全模拟正常的页面加载过程。请注意,Chrome应用程序不久将不再存在于Chrome中。
在一个新的选项卡中打开站点的问题是它对用户是可见的。您可以通过打开一个新的非聚焦窗口(chrome.windows.create)来减少烦恼,但这也会对许多用户产生一些视觉效果,这取决于他们的桌面环境。
因此,唯一不显眼的解决方法是创建指向远程页面的iframe。
iframe
,因为MV3扩展在后台脚本中没有DOM。对于此任务,您根本不需要后台脚本。iframe可以“隐藏”在关闭的ShadowDOM中的页面中。这个iframe将完全访问所有授予的chrome
API。iframe
,其中src
指向要获取的站点。manifest.json:
"host_permissions": ["<all_urls>"],
"permissions": ["declarativeNetRequestWithHostAccess", "scripting", "webRequest"],
"web_accessible_resources": [{
"resources": ["iframer.html"],
"matches": ["<all_urls>"]
}],
主要内容脚本:
(async () => {
const data = await getRemoteSiteData('https://www.example.com', ['body']);
console.log(data);
})();
function getRemoteSiteData(url, selectors) {
const id = Math.random().toString(36).slice(2);
const iframe = document.createElement('iframe');
const el = document.createElement('div');
const root = el.attachShadow({mode: 'closed'});
root.appendChild(document.createElement('style')).textContent =
':host { display: none !important }';
root.appendChild(iframe);
iframe.src = chrome.runtime.getURL('iframer.html#' + id);
document.body.appendChild(el);
return new Promise(resolve => {
chrome.runtime.onMessage.addListener(function _(msg, sender, sendResponse) {
if (msg.id !== id) return;
if (msg.init) {
sendResponse({url, selectors, frameId: sender.frameId});
} else {
el.remove();
chrome.runtime.onMessage.removeListener(_);
resolve(msg.result);
}
});
});
}
iframer.html:
<script src=iframer.js></script>
iframer.js:
(async () => {
const id = location.hash.slice(1);
const tabId = (await chrome.tabs.getCurrent()).id;
const job = await chrome.tabs.sendMessage(tabId, {id, init: true});
const iframe = document.createElement('iframe');
let webFrameId;
chrome.webRequest.onBeforeRequest.addListener(function _(info) {
chrome.webRequest.onBeforeRequest.removeListener(_);
webFrameId = info.frameId;
}, {tabId, types: ['sub_frame']});
iframe.src = job.url;
document.body.appendChild(iframe);
await new Promise(onload => Object.assign(iframe, {onload}));
const data = await chrome.scripting.executeScript({
target: {tabId, frameIds: [webFrameId]},
args: [job.selectors],
func: selectors => selectors.map(sel =>
Array.from(document.querySelectorAll(sel),
el => el.textContent)),
})
const {result} = data.find(d => d.frameId === webFrameId);
await chrome.tabs.sendMessage(tabId, {id, result}, {frameId: job.frameId});
})();
http://
URL无法加载到https://
站点上。您将看到devtools控制台中的混合内容被阻塞的错误。唯一的解决办法是将上面的步骤1替换为为远程站点打开一个新的选项卡/窗口,并在那里运行一个内容脚本,例如,原始内容脚本向使用chrome.tabs.create + chrome.scripting.executeScript的后台脚本发送一条消息。window == window.top
来停止在iframes中加载。
如果该检查是在内联script
元素中完成的,则不能欺骗它。https://stackoverflow.com/questions/71091125
复制相似问题