I am trying to make a Chrome extension which calls an offscreen file from content.js to play an audio file. However, running it in Chrome, the console window throws this error:
<code>Uncaught TypeError: Cannot read properties of undefined (reading 'createDocument')
at checkForVideoElement (content.js:28:30)
at MutationObserver.<anonymous> (VM304 content.js:7:21)
</code>
<code>Uncaught TypeError: Cannot read properties of undefined (reading 'createDocument')
at checkForVideoElement (content.js:28:30)
at MutationObserver.<anonymous> (VM304 content.js:7:21)
</code>
Uncaught TypeError: Cannot read properties of undefined (reading 'createDocument')
at checkForVideoElement (content.js:28:30)
at MutationObserver.<anonymous> (VM304 content.js:7:21)
manifest.json:
<code>{
"manifest_version": 3,
"name": "Custom",
"description": "",
"version": "1.0",
"action": {
"default_popup": "templates/popup.html",
"default_icon": "images/logo.png"
},
"background": {
"service_worker": "scripts/background.js"
},
"icons": {
"16": "images/logo.png",
"32": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"permissions": [
"storage",
"activeTab",
"scripting",
"offscreen"
],
"host_permissions": [
"https://www.netflix.com/watch/*"
],
"content_scripts": [
{
"matches": [
"https://www.netflix.com/watch/*"
],
"js": [
"scripts/content.js"
]
}
]
}
</code>
<code>{
"manifest_version": 3,
"name": "Custom",
"description": "",
"version": "1.0",
"action": {
"default_popup": "templates/popup.html",
"default_icon": "images/logo.png"
},
"background": {
"service_worker": "scripts/background.js"
},
"icons": {
"16": "images/logo.png",
"32": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"permissions": [
"storage",
"activeTab",
"scripting",
"offscreen"
],
"host_permissions": [
"https://www.netflix.com/watch/*"
],
"content_scripts": [
{
"matches": [
"https://www.netflix.com/watch/*"
],
"js": [
"scripts/content.js"
]
}
]
}
</code>
{
"manifest_version": 3,
"name": "Custom",
"description": "",
"version": "1.0",
"action": {
"default_popup": "templates/popup.html",
"default_icon": "images/logo.png"
},
"background": {
"service_worker": "scripts/background.js"
},
"icons": {
"16": "images/logo.png",
"32": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"permissions": [
"storage",
"activeTab",
"scripting",
"offscreen"
],
"host_permissions": [
"https://www.netflix.com/watch/*"
],
"content_scripts": [
{
"matches": [
"https://www.netflix.com/watch/*"
],
"js": [
"scripts/content.js"
]
}
]
}
content.js:
<code>// Try to find the video element immediately
if (!checkForVideoElement()) {
// If not found, use a mutation observer to watch for changes
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
if (checkForVideoElement()) {
// Stop observing once the video element is found
observer.disconnect();
break;
}
}
}
});
// Start observing the document body for added nodes
observer.observe(document.body, { childList: true, subtree: true });
}
function checkForVideoElement() {
var video = document.querySelector("video");
if (video) {
console.log(video);
if (video.currentTime <= 60) {
console.log("Video found less than 60s");
// creating offscreen document to play the custom Tudum sound
// chrome.offscreen.createDocument({
// url: chrome.runtime.getURL("offscreen.html"),
// reasons: ["AUDIO_PLAYBACK"],
// justification: "Playing Custom Sound"
// }, () => {
// chrome.runtime.sendMessage("playCustomSound");
// });
}
return true;
}
return false;
}
</code>
<code>// Try to find the video element immediately
if (!checkForVideoElement()) {
// If not found, use a mutation observer to watch for changes
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
if (checkForVideoElement()) {
// Stop observing once the video element is found
observer.disconnect();
break;
}
}
}
});
// Start observing the document body for added nodes
observer.observe(document.body, { childList: true, subtree: true });
}
function checkForVideoElement() {
var video = document.querySelector("video");
if (video) {
console.log(video);
if (video.currentTime <= 60) {
console.log("Video found less than 60s");
// creating offscreen document to play the custom Tudum sound
// chrome.offscreen.createDocument({
// url: chrome.runtime.getURL("offscreen.html"),
// reasons: ["AUDIO_PLAYBACK"],
// justification: "Playing Custom Sound"
// }, () => {
// chrome.runtime.sendMessage("playCustomSound");
// });
}
return true;
}
return false;
}
</code>
// Try to find the video element immediately
if (!checkForVideoElement()) {
// If not found, use a mutation observer to watch for changes
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
if (checkForVideoElement()) {
// Stop observing once the video element is found
observer.disconnect();
break;
}
}
}
});
// Start observing the document body for added nodes
observer.observe(document.body, { childList: true, subtree: true });
}
function checkForVideoElement() {
var video = document.querySelector("video");
if (video) {
console.log(video);
if (video.currentTime <= 60) {
console.log("Video found less than 60s");
// creating offscreen document to play the custom Tudum sound
// chrome.offscreen.createDocument({
// url: chrome.runtime.getURL("offscreen.html"),
// reasons: ["AUDIO_PLAYBACK"],
// justification: "Playing Custom Sound"
// }, () => {
// chrome.runtime.sendMessage("playCustomSound");
// });
}
return true;
}
return false;
}
offscreen.html:
<code><!DOCTYPE html>
<html lang="en">
<head>
<title>Play Sound</title>
</head>
<body>
<script src="../scripts/offscreen.js"></script>
</body>
</html>
</code>
<code><!DOCTYPE html>
<html lang="en">
<head>
<title>Play Sound</title>
</head>
<body>
<script src="../scripts/offscreen.js"></script>
</body>
</html>
</code>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Play Sound</title>
</head>
<body>
<script src="../scripts/offscreen.js"></script>
</body>
</html>
offscreen.js:
<code>console.log("Offscreen script loaded");
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Offscreen script received message:", message);
if (message.action === "playSound") {
playSound();
}
});
function playSound() {
chrome.storage.local.get("Sound", function (result) {
let audio = result.Sound;
if (audio) {
console.log("Audio found");
// Convert the data URI to a Blob
fetch(audio)
.then(response => response.blob())
.then(blob => {
const audioUrl = URL.createObjectURL(blob);
const audioElement = new Audio(audioUrl);
audioElement.play().then(() => {
console.log("Audio played successfully");
}).catch(error => {
console.error("Error playing audio:", error);
});
})
.catch(error => {
console.error("Error converting audio data:", error);
});
} else {
console.log("Audio not found");
}
});
}
</code>
<code>console.log("Offscreen script loaded");
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Offscreen script received message:", message);
if (message.action === "playSound") {
playSound();
}
});
function playSound() {
chrome.storage.local.get("Sound", function (result) {
let audio = result.Sound;
if (audio) {
console.log("Audio found");
// Convert the data URI to a Blob
fetch(audio)
.then(response => response.blob())
.then(blob => {
const audioUrl = URL.createObjectURL(blob);
const audioElement = new Audio(audioUrl);
audioElement.play().then(() => {
console.log("Audio played successfully");
}).catch(error => {
console.error("Error playing audio:", error);
});
})
.catch(error => {
console.error("Error converting audio data:", error);
});
} else {
console.log("Audio not found");
}
});
}
</code>
console.log("Offscreen script loaded");
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Offscreen script received message:", message);
if (message.action === "playSound") {
playSound();
}
});
function playSound() {
chrome.storage.local.get("Sound", function (result) {
let audio = result.Sound;
if (audio) {
console.log("Audio found");
// Convert the data URI to a Blob
fetch(audio)
.then(response => response.blob())
.then(blob => {
const audioUrl = URL.createObjectURL(blob);
const audioElement = new Audio(audioUrl);
audioElement.play().then(() => {
console.log("Audio played successfully");
}).catch(error => {
console.error("Error playing audio:", error);
});
})
.catch(error => {
console.error("Error converting audio data:", error);
});
} else {
console.log("Audio not found");
}
});
}
I don’t understand what’s happening since I should not have to define a chrome object in my content.js file if I’m not wrong.
A previous version of my content.js file was able to run chrome.storage.local.get()
so I don’t know why this method is not working.