我有一个在公司网站上运行的用户脚本,用于从页面中删除某些信息(基于JavaScript正则表达式替换)。
我决定将userScript变成Chrome扩展程序,我想添加运行不同版本脚本的选项。
我的第一个目标是在用户按下 popup.html 中的一个选项时运行脚本。
目前,我的chrome.tabs.executeScript()
调用尝试访问该文档并将其bodyHTML替换为其bodyHTML减去替换的正则表达式。
然而,对脚本的肉功能normalExecution()
的调用似乎被跨源约束阻止了?
我看到以下错误:
未捕获的ReferenceError:未定义normalExecution
未捕获的DOMException:使用origin阻止了一个帧 " https://website.com"从访问跨源框架
以下是我的 popup.html 和 popup.js 代码。
总之,HTML代码有3个div作为按钮。目前,我已将它们全部设置为具有应执行normalExecution()
中的代码的click-action-handler,但我得到上述错误。
Popup.js :
function click(e) {
chrome.tabs.executeScript(null, {code:"normalExecution();"}, null);
window.close();
}
var normalExecution = function(){
console.log('normal execution engaged');
var allSpans = document.getElementsByTagName("span");
for(var i = 0; i < allSpans.length; i++) {
try{
if (allSpans[i].className.indexOf("textblock") > -1) {
//check if the ">" is part of switch output, if not then procede
if(allSpans[i].innerHTML.regexIndexOf(/sw.>.*/g) < 0){
//console.log('true');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/>.*/g, ' ---- Removed ---- ');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/(-----).*/g, ' ---- Removed Dashes (beta) ---- ');
}
else{
console.log('switch CLI output detected');
}
}
}catch(e){}
}
};
document.addEventListener('DOMContentLoaded', function () {
console.log('adding event listener');
var divs = document.querySelectorAll('div');
for(var i = 0; i < divs.length; i++){
divs[i].addEventListener('click', click);
}
});
String.prototype.regexIndexOf = function(regex, startpos) {
var indexOf = this.substring(startpos || 0).search(regex);
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
};
popup.html [仅限正文]:
<body>
<div id="Disabled">Disabled</div>
<div id="Normal">Normal</div>
<div id="Super-power">Super-power</div>
答案 0 :(得分:1)
您目前正在尝试在 popup.js 中定义的内容脚本code
中执行某项功能。因此,它未在内容脚本上下文/范围中定义。因此,当您尝试使用chrome.tabs.executeScript()
没有理由在 popup.js 中定义normalExecution
。你没有在那里使用它,似乎没有意图在那里使用它。更好的方法是将此内容脚本移到一个单独的文件中,然后注入该文件:
popup.js :
function click(e) {
chrome.tabs.executeScript({file:"/contentScript.js"});
window.close();
}
document.addEventListener('DOMContentLoaded', function () {
console.log('adding event listener');
var divs = document.querySelectorAll('div');
for(var i = 0; i < divs.length; i++){
divs[i].addEventListener('click', click);
}
});
contentScript.js :
(function() {
//Don't change the prototype of a built in type just to use it _once_.
// Changing the prototype of a built in type is, generally, not a good idea. Sometimes,
// it is the right thing to do, but there are potential issues. Without learning what
// those issues are, it is better to avoid changing the prototype, particularly if
// you are only using it once.
//String.prototype.regexIndexOf = function(regex, startpos) {
function regexIndexOf(str, regex, startpos) {
var indexOf = str.substring(startpos || 0).search(regex);
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
};
console.log('normal execution engaged');
var allSpans = document.getElementsByTagName("span");
for(var i = 0; i < allSpans.length; i++) {
try {
if (allSpans[i].className.indexOf("textblock") > -1) {
//check if the ">" is part of switch output, if not then proceed
if(regexIndexOf(allSpans[i].innerHTML,/sw.>.*/g) < 0){
//console.log('true');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/>.*/g
, ' ---- Removed ---- ');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/(-----).*/g
, ' ---- Removed Dashes (beta) ---- ');
} else {
console.log('switch CLI output detected');
}
}
} catch(e) {}
}
})();
popup.html :
<body>
<div id="Disabled">Disabled</div>
<div id="Normal">Normal</div>
<div id="Super-power">Super-power</div>
.replace()
上使用innerHTML
通常是一个坏主意在.replace()
属性上使用innerHTML
并将其分配回innerHTML
会产生多种潜在的负面影响。这些包括可能通过更改实际HTML元素中包含的值而不是文本来破坏HTML代码;并通过删除正在侦听后代元素的事件处理程序来打破现有的JavaScript。您应该只对文本节点应用此类更改。如果您控制要更改的HTML,则在.replace()
属性上执行innerHTML
可能是合理的。如果您无法控制页面上(以及其他扩展名)中使用的所有HTML和JavaScript,那么您很可能会遇到问题。
我的回答:Replacing a lot of text in browser's addon显示了仅在文本节点上执行.replace()
的一种方法。以下仅更改文本节点的附加示例在某些方面更复杂,并且在这种情况下以其他方式不太复杂。但是,有人会将文本节点限制为仅包含在特定标记中的文本节点(在示例中为<p>
标记)。在以下各项中,文本节点将替换为<span>
,其中包含其他HTML(此处不需要):Replace each word in webpage's paragraphs with a button containing that text,Change matching words in a webpage's text to buttons和Highlight a word of text on the page using .replace()。这些示例不太复杂,因为您希望影响<span>
元素的内容。因此,您需要考虑您有多个<spans>
的情况,其中一些可能是其他人的后代。上述示例都没有处理这种情况,因为所有这些都处理了可能无法选择后代的情况(页面上的所有文本或<p>
元素中的文本)。
您的内容脚本(normalExecution()
和regexIndexOf()
)中还有其他内容可以更有效地完成。以下是:
regexIndexOf()
来测试字符串中RegExp的不匹配。您当前没有使用regexIndexOf()
的附加功能来提供起始索引。对于您使用的功能,有多种现有方法:String.prototype.search(),RegExp.prototype.test()和String.prototype.match()。 您正在使用document.getElementsByTagName("span");
,然后在"textblock"
中检测到className
的存在。您可以使用querySelectorAll()执行此操作:
document.querySelectorAll("span[class*=textblock]");
但是,更有可能的情况是,您实际上是在尝试检测<span>
是否具有完全textblock
的类。在这种情况下,您可能希望使用:
document.querySelectorAll("span.textblock");
contentScript.js :
(function() {
console.log('normal execution engaged');
//You may actually want the selector "span.textblock", which matches spans with the
// class "textblock" (e.g. <span class="foo textblock someOtherClass">). However, the
// following matches your current code which checks to see that the className contains
// "textblock" somewhere (e.g. <span class="footextblockbar someOtherClass">).
var allSpans = document.querySelectorAll("span[class*=textblock]");
for(var i = 0; i < allSpans.length; i++) {
try {
//check if the ">" is part of switch output, if not then proceed
//This does not account for the possibility of having the > both after
// a 'sw.' and by itself.
//It is unclear if you are really wanting /sw.>/ or /sw\.>/ here (i.e. is
// the "." intended to match any character, or an actual "."). Which
// is really desired will depend on the text you are trying to not change.
if(allSpans[i].innerHTML.search(/sw.>.*/g) < 0 ){
//console.log('true');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/>.*/g
, ' ---- Removed ---- ');
allSpans[i].innerHTML = allSpans[i].innerHTML.replace(/(-----).*/g
, ' ---- Removed Dashes (beta) ---- ');
} else {
console.log('switch CLI output detected');
}
} catch(e) {}
}
})();
答案 1 :(得分:0)
我不认为它是关于交叉起源的限制。
当您执行"normalExecution();"
时,它实际上将此字符串注入页面并在页面的上下文中执行它。由于未在页面上定义normalExecution
(而是在popup.js
处定义),因此您获得ReferenceError
。
你需要做这样的事情
chrome.tabs.executeScript(null, {code: "(" + normalExecuton + ")();"}, null);