问题描述
每当我将鼠标悬停在我的 ext 图标上时,我都会收到工具提示“想要访问此站点”,这是错误的,因为它应该只希望在 youtube.com/watch?v=* 上访问(这是另一个与 Manifest 匹配的故事) ' 拒绝接受 https://www.youtube.com/watch?v=*
作为有效 URL)
这是我目前正在做的:
// manifest.json
{
"name": "YouTube Overlay","version": "0.1","manifest_version" : 3,"description": "Lays overlay on YouTube,can be used for guitar chords/lyrics.","background" : {
"service_worker" : "bg.js"
},"action": {},"permissions": ["activeTab","scripting"],"web_accessible_resources": [{
"resources": ["funcsTBInsertedIntodoM.js"],"matches": ["https://www.youtube.com/*"]
}]
}
// bg.js
chrome.action.onClicked.addListener(function (tab) {
chrome.scripting.executeScript({
target: {tabId: tab.id},files: ['yt.js'],});
});
yt.js
在上面执行时,将一堆 HTML 元素和 CSS 规则注入到 DOM 中。它还将 funcsTBInsertedIntodoM.js
(在上面 manifest.json 中的 web_accessible_resources
中指定)注入 DOM,其中包含注入的 HTML 按钮的函数定义。
所以基本上每当用户点击我的 ext 图标时,bg.js 就会执行,而后者又会执行 yt.js。当用户在 YouTube 视频上单击时,它工作正常。但否则它自然会在控制台中引发错误。那么我如何指示清单仅在 YouTube 视频上执行 bg.js? (它甚至不应该在其他 YouTube 页面上运行,仅当用户在视频页面上时才运行)。
此外,我收到了来自 Google Web Store 的拒绝通知,用于我的扩展程序:
Violation(s):
Use of Permissions:
Violation reference ID: Purple Potassium
Violation: Requesting but not using the following permission(s):
activeTab
How to rectify: Remove the above permission(s)
但如果我删除 activeTab
权限,我的扩展程序根本无法使用。
如果有人能提出解决这两个问题的解决方案,我将不胜感激。感谢阅读。
这是 yt.ts:
// all global variables are 'var' instead of let or const because the delete command works only on var
var isFullScreen = false;
var extensionActivated = false;
var resCheckerID:number;
var chordsTALeftPaddingNonFS = chordsTALeftPaddingNonFSOnExit;
var chordsTALeftPaddingFS = "0px";
var thisIsThe1stExitAfterFS = true;
var activateExtension = () => {
console.log("YouTube Overlay activated.");
let scriptElemForASBtn = document.createElement("style");
let innardsForASBtn = styleForAsBtn;
scriptElemForASBtn.innerHTML = innardsForASBtn;
document.head.appendChild(scriptElemForASBtn);
const videoElem = document.querySelector("video");
const vidBottomPanel = document.querySelector(".ytp-chrome-bottom");
const progBarPadding = document.querySelector(".ytp-progress-bar-padding");
const getIdealChordsDivStyles = (isFullScreen:boolean) => {
let vidDims = videoElem.getBoundingClientRect();
let progBarPaddingDims = progBarPadding.getBoundingClientRect();
if (isFullScreen){
console.log("fullscreen detected")
thisIsThe1stExitAfterFS = true;
chordsTALeftPaddingNonFS = chordsTA.style.paddingLeft; // saving this for next nonFS
chordsTA.style.paddingLeft = chordsTALeftPaddingFS; // assigning this from prev FS
return `overflow: hidden; position: absolute; z-index: 1111; width: 100vw; height: ${progBarPaddingDims.y - vidDims.y + (progBarPaddingDims.height/2)}px`;
} else {
try {
if(thisIsThe1stExitAfterFS)
chordsTALeftPaddingFS = chordsTA.style.paddingLeft;
chordsTA.style.paddingLeft = chordsTALeftPaddingNonFS;
thisIsThe1stExitAfterFS = false;
} catch {} // saving this for next FS. on first run it obsly won't be able to find chordsTA.
return `overflow: hidden; position: absolute; z-index: 1111; left: ${vidDims.x}px; top: ${vidDims.y}px; width: ${vidDims.width}px; height: ${progBarPaddingDims.y - vidDims.y + (progBarPaddingDims.height/2)}px`;
}
}
// creating the chords div
let chordsDiv = document.createElement('div');
chordsDiv.style.csstext = getIdealChordsDivStyles(isFullScreen);
chordsDiv.setAttribute("id","chordsDiv");
let htmlInnards = `
<div id="chordsCtrls" onmouSEOver="unhideChordsCtrls();" onmouSEOut="hideChordsCtrls();" style="z-index: 1112; height: ${vidBottomPanel.getBoundingClientRect().height}px; position: absolute; display: inline-block;">
<a id="asBtn" onclick="toggleAutoScroll()" class="btn-flip" data-back="Turn on" data-front="Auto-Scroll Off"></a>
<a id="decTxtSize" class="btn noselect" onclick="decTxtSize();">Tᵀ</a>
<a id="incTxtSize" class="btn noselect" onclick="incTxtSize();">ᵀT</a>
<a id="decIndent" class="btn noselect" onclick="decIndent();">¶-</a>
<a id="incIndent" class="btn noselect" onclick="incIndent();">¶+</a>
</div>
<textarea onkeyup="checkTAWidth();" onclick="checkTAWidth();" id="chordsTA" spellcheck="false" style="position:absolute; left:50%; transform: translate(-50%,0); white-space: pre; overflow-wrap: normal; overflow-x: scroll; font-family: Roboto Mono,monospace; background-color: rgba(0,0.35); color: white; height: 100%; min-width:10vw; font-size: ${window.screen.height*0.026}px;" placeholder="\n\nPaste\nyour\nchords/lyrics\nin\nhere!">
`
chordsDiv.innerHTML = htmlInnards;
document.body.appendChild(chordsDiv);
chordsTA.value = lyricsOnExit; // doing in convoluted way because i cant fig it out :S
if (chordsTA.value === "undefined") chordsTA.value = "";
chordsTA.scrollTop = lyricslocOnExit;
chordsTA.style.fontSize = lyricsSizeOnExit;
chordsTA.style.paddingLeft = chordsTALeftPaddingNonFS;
console.log("Lyrics reinstated,if any.");
// hiding the scrollbar of chords div & textarea
let styleForScrollbarHiding = `#chordsDiv::-webkit-scrollbar,#chordsTA::-webkit-scrollbar {height: 0; width: 0;}`;
let styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styleForScrollbarHiding;
document.head.appendChild(styleSheet);
// auto sizing of chords div
function resCheck() {
let vidDims = videoElem.getBoundingClientRect();
let chordsDims = chordsDiv.getBoundingClientRect();
let requisiteHtForChordsDiv = vidDims.height - vidBottomPanel.getBoundingClientRect().height- (progBarPadding.getBoundingClientRect().height/2);
if (((chordsDims.x !== vidDims.x || chordsDims.width !== vidDims.width) && chordsDims.x !== 0) || (chordsDims.x === 0 && chordsDims.x !== vidDims.x)) { // base cond's latter gets True when exiting from FS. Base's former's former checks in non fullScn mode if x or width is wrong.
if (isFullScreen && vidDims.y === 0) return;
console.log("Video dimensions changed detected,redrawing overlay.");
isFullScreen = vidDims.y === 0 ? true : false;
chordsDiv.style.csstext = getIdealChordsDivStyles(isFullScreen);
}
}
resCheckerID = setInterval(resCheck,2000);
// addding my JS functions to the youtube HTML webpage/DOM
let s = document.createElement('script');
// Todo: add "scriptName.js" to web_accessible_resources in manifest.json
s.src = chrome.runtime.getURL('funcsTBInsertedIntodoM.js');
(document.head || document.documentElement).appendChild(s);
}
var styleForAsBtn = `
#asBtn {
opacity: 1;
outline: 0;
color: #FFFFFF;
line-height: 40px;
position: relative;
text-align: center;
letter-spacing: 1px;
display: inline-block;
text-decoration: none;
text-transform: uppercase;
font-size: small;
font-weight: bold;
}
#asBtn:hover:after {
opacity: 1;
transform: translateY(0) rotateX(0);
}
#asBtn:hover:before {
opacity: 0;
transform: translateY(50%) rotateX(90deg);
}
#asBtn:after {
top: 0;
left: 0;
opacity: 0;
width: 100%;
color: #000000;
background: #BCBCBC;
display: block;
transition: 0.25s;
position: absolute;
content: attr(data-back);
transform: translateY(-50%) rotateX(90deg);
}
#asBtn:before {
top: 0;
left: 0;
opacity: 1;
color: #FFFFFF;
background: #323237;
display: block;
padding: 0 5px;
line-height: 40px;
transition: 0.25s;
position: relative;
content: attr(data-front);
transform: translateY(0) rotateX(0);
}
/* CSS for other buttons */
.btn{
display: inline-block;
color: #FFFFFF;
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 50%;
text-align: center;
vertical-align: middle;
overflow: hidden;
font-weight: bold;
background-image: -webkit-linear-gradient(#666666 0%,#323237 100%);
background-image: linear-gradient(#666666 0%,#323237 100%);
text-shadow: 1px 1px 1px rgba(255,255,0.66);
Box-shadow: 0 1px 1px rgba(0,0.28);
font-size: x-large;
cursor: pointer;
}
.btn:active{
color: #000000;
}
.btn:hover {
text-align: center;
opacity: 1;
background-image: -webkit-linear-gradient(#999999 0%,#323237 100%);
background-image: linear-gradient(#999999 0%,#323237 100%);
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version,currently
supported by Chrome,Edge,Opera and Firefox */
}
#incTxtSize,#decTxtSize{
font-size: large;
}
#chordsCtrls>*{
transition: transform 0.1s linear;
}
textarea::placeholder {
color: white;
opacity: 0.8;
font-size: 4vh;
}
#contentContainer.tp-yt-app-drawer[swipe-open].tp-yt-app-drawer::after{
visibility: hidden;
}
`
// the last css property above is hiding a thin left side vertical area which otherwise causes chordsCtrls not to show up if mouse is on extreme left. Also causes difficulty clicking SpeedDn button.
if (!document.querySelector("#chordsDiv")){
activateExtension();
} else {
console.log("YouTube Overlay deactivated");
var lyricsOnExit = chordsTA.value;
var lyricslocOnExit = chordsTA.scrollTop;
var lyricsSizeOnExit = chordsTA.style.fontSize;
var chordsTALeftPaddingNonFSOnExit = chordsTA.style.paddingLeft; // won't be possible to save FS padding unless i deactivate extension with an X btn. Due to scope prob.
document.querySelector("#chordsDiv").remove();
clearInterval(resCheckerID);
delete window.resCheckerID;
}
这是 funcsTBInsertedIntodoM.ts:
console.log("Loading essential funcs needed for YouTube Overlay extension.")
clearInterval(asIntervalID); // cannot clear this from yt.ts because yt.ts runs in a sandBox. So need to clear it here,if it exists,on startup. Thankfully doesn't throw error even if doesn't exist.
clearTimeout(hideChordsCtrlsTimeoutID);
var asspeeds = {1: 250,2: 150,3: 100,4: 90,5: 75,6: 60,7: 50,8: 40,9: 30};
var chordsCtrls:HTMLdivelement = document.querySelector("#chordsCtrls");
var chordsTA:HTMLTextAreaElement = document.querySelector("#chordsTA");
var asBtn:HTMLButtonElement = document.querySelector("#asBtn");
var autoScrollSpeed = 250;
var asIntervalID = 0;
function toggleAutoScroll() {
if(asIntervalID){
clearInterval(asIntervalID);
asIntervalID = 0;
console.log("Stopped autoscroll.");
document.querySelector("#speedUp").remove(); document.querySelector("#speedDn").remove();
setAttributes(asBtn,{"data-front": `Auto-Scroll Off`,'data-back': 'Turn On'});
return;
}
// create speed + - buttons
let speedUp = document.createElement("a");
speedUp.textContent = "+";
setAttributes(speedUp,{'id': 'speedUp','class': 'btn noselect','onclick': 'speedUpAS();'});
document.querySelector("#chordsCtrls").insertBefore(speedUp,document.querySelector("#decTxtSize"));
let speedDn = document.createElement("a");
speedDn.textContent = "-";
setAttributes(speedDn,{'id': 'speedDn','onclick': 'speedDnAS();'});
document.querySelector("#chordsCtrls").insertBefore(speedDn,asBtn);;
setAttributes(asBtn,{"data-front": `Speed: ${getKeyByValue(asspeeds,autoScrollSpeed)}`,'data-back': 'Turn Off'});
asIntervalID = setInterval(_=>{chordsTA.scrollBy(0,1)},autoScrollSpeed);
console.log("Started autoscroll.")
}
var speedUpAS = () => {
console.log("Speeding up autoscroll")
let asBtnText = asBtn.getAttribute('data-front');
let newSpeed:number = parseInt(asBtnText.charat(asBtnText.length - 1))+1;
if (newSpeed in asspeeds){
clearInterval(asIntervalID);
autoScrollSpeed = asspeeds[newSpeed];
asIntervalID = 0;
asBtn.setAttribute('data-front',`Speed: ${getKeyByValue(asspeeds,autoScrollSpeed)}`);
asIntervalID = setInterval(_=>{chordsTA.scrollBy(0,autoScrollSpeed);
}
}
var speedDnAS = () => {
console.log("Speeding down autoscroll")
let asBtnText = asBtn.getAttribute('data-front');
let newSpeed:number = parseInt(asBtnText.charat(asBtnText.length - 1))-1;
if (newSpeed in asspeeds){
clearInterval(asIntervalID);
autoScrollSpeed = asspeeds[newSpeed];
asIntervalID = 0;
asBtn.setAttribute('data-front',autoScrollSpeed);
}
}
var incTxtSize = () => {
let currFontSize = parseFloat(chordsTA.style.fontSize);
let newFontSize = currFontSize += 1;
chordsTA.style.fontSize = `${newFontSize}px`;
qickSizeUp();
}
var decTxtSize = () => {
let currFontSize = parseFloat(chordsTA.style.fontSize);
let newFontSize = currFontSize -= 1;
chordsTA.style.fontSize = `${newFontSize}px`;
qickSizedn();
}
var unhideChordsCtrls = () => {
clearTimeout(hideChordsCtrlsTimeoutID);
let childrenOfchordsCtrlsDiv:any = chordsCtrls.getElementsByTagName("*");
for (let index = 0; index < childrenOfchordsCtrlsDiv.length; index++) {
childrenOfchordsCtrlsDiv[index].style.transform = "translate(0,0)";
}
}
var hideChordsCtrlsTimeoutID = 0;
var hideChordsCtrls = () => {
hideChordsCtrlsTimeoutID = setTimeout(() => {
let childrenOfchordsCtrlsDiv:any = chordsCtrls.getElementsByTagName("*");
for (let index = 0; index < childrenOfchordsCtrlsDiv.length; index++) {
childrenOfchordsCtrlsDiv[index].style.transform = "translate(0,-100%)";
}
},2000);
}
hideChordsCtrlsTimeoutID = setTimeout(() => { //hide the controls after initially showing them for 4 secs
hideChordsCtrls();
},4000);
var decIndent = () => {
let newLeftPadding = (parseInt(chordsTA.style.paddingLeft) - 50);
if (!newLeftPadding) newLeftPadding = 0; // this catches NaN on first run,as it is not set. Also doesn't allow to go less than 0 somehow,luckily.
chordsTA.style.paddingLeft = `${newLeftPadding}px`;
}
var incIndent = () => {
let newLeftPadding = (parseInt(chordsTA.style.paddingLeft) + 50);
if (!newLeftPadding) newLeftPadding = 50; // this catches NaN on first run,as it is not set.
if (newLeftPadding > document.querySelector("#chordsDiv").getBoundingClientRect().width) return;
chordsTA.style.paddingLeft = `${newLeftPadding}px`;
}
// following funcs stolen from SO for finding a key by its value & setting attributes multiple at a time.
function getKeyByValue(object:object,value:Number) {
return Object.keys(object).find(key => object[key] === value);
}
function setAttributes(el:HTMLElement,attrs:object) {
for(var key in attrs) {
el.setAttribute(key,attrs[key]);
}
}
或者您可能更喜欢在 GitHub 上阅读代码:https://github.com/XtremePwnership/YoutubeOverlay
解决方法
由于 YouTube 视频页面托管在 youtube.com/watch
,因此可以在清单中指定:
{
"name": "YouTube Overlay","version": "0.1","manifest_version" : 3,"description": "Lays overlay on YouTube,can be used for guitar chords/lyrics.","background" : {
"service_worker" : "bg.js"
},"action": {},"permissions": ["activeTab","scripting"],"web_accessible_resources": [{
"resources": ["funcsTBInsertedIntoDOM.js"],"matches": ["https://www.youtube.com/watch?v=*"]
}]
}