I'm new to extension creation and have a problem, which I've already been able to find various ways to solve, but which are all different from mine and/or fixed with manifest V2 instead of V3 which I need.
Also, some fixes found work on their end, but not on mine, so I really don't understand the problem.
Here is my problem:
I want to make a chrome extension to take screenshots of my browser and apps
I found an online tutorial that seemed correct to me (by the way, the only tutorial that uses AND the screenshots AND the V3 manifest, so perfect!)
Following the tutorial, I got the following error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
I looked for various ways, but nothing worked, I ended up downloading the git code of the tutorial, but it does not change anything, the error is still present
From what I understand, the error is in the following line:
chrome.action.onClicked.addListener(function (tab) {
chrome.desktopCapture.chooseDesktopMedia(
["screen", "window", "tab"],
tab,
(streamId) => {
if (streamId && streamId.length) {
setTimeout(() => {
chrome.tabs.sendMessage(
tab.id,
{ name: "stream", streamId },
(response) => console.log("received user data", response) // error is here, response is undefined
);
}, 200);
}
}
);
});
I get undefined instead of the response, and I think it's from there that it's a problem, because it never goes on and therefore never activates the onMessage function, nor the content_script
Here is the full background.js code :
chrome.action.onClicked.addListener(function (tab) {
chrome.desktopCapture.chooseDesktopMedia(
["screen", "window", "tab"],
tab,
(streamId) => {
if (streamId && streamId.length) {
setTimeout(() => {
chrome.tabs.sendMessage(
tab.id,
{ name: "stream", streamId },
(response) => console.log("received user data", response)
);
}, 200);
}
}
);
});
chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
if (message.name === "download" && message.url) {
chrome.downloads.download(
{
filename: "screenshot.png",
url: message.url,
},
(downloadId) => {
senderResponse({ success: true });
}
);
return true;
}
});
Content_script
chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
if (message.name === 'stream' && message.streamId) {
let track, canvas
navigator.mediaDevices.getUserMedia({
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: message.streamId
},
}
}).then((stream) => {
track = stream.getVideoTracks()[0]
const imageCapture = new ImageCapture(track)
return imageCapture.grabFrame()
}).then((bitmap) => {
track.stop()
canvas = document.createElement('canvas');
canvas.width = bitmap.width; //if not set, the width will default to 200px
canvas.height = bitmap.height;//if not set, the height will default to 200px
let context = canvas.getContext('2d');
context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height)
return canvas.toDataURL();
}).then((url) => {
chrome.runtime.sendMessage({name: 'download', url}, (response) => {
if (response.success) {
alert("Screenshot saved");
} else {
alert("Could not save screenshot")
}
canvas.remove()
senderResponse({success: true})
})
}).catch((err) => {
alert("Could not take screenshot")
senderResponse({success: false, message: err})
})
return true;
}
})
manifest v3
{
"name": "Screenshots",
"version": "0.0.1",
"description": "Take screenshots",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["desktopCapture", "downloads", "tabs", "nativeMessaging"],
"action": {
"default_title": "Take a Screenshot"
},
"icons": {
"16": "/assets/icon-16.png",
"32": "/assets/icon-32.png",
"48": "/assets/icon-48.png",
"128": "/assets/icon-128.png"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_script.js"]
}
]
}
I tried several things after various research like
Disable my extensions (which makes no sense, but you never know)
Add a timeout for the response, I tried up to 20 seconds delay, but without success
Added breakpoints everywhere to see if it crosses the line or not
Here is an implementation without service worker and content scripts.
manifest.json
{
"name": "desktopCapture",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"desktopCapture",
"tabs",
"downloads"
],
"action": {
"default_popup": "popup.html"
}
}
popup.html
<html>
<body>
<script src="popup.js"></script>
</body>
</html>
popup.js
const createDate = {
url: "desktopCaptuer.html",
type: "popup",
width: 800,
height: 600
};
chrome.windows.create(createDate);
desktopCaptuer.html
<html>
<body>
<input type="button" id="captuer" value="Captuer">
<script src="desktopCaptuer.js"></script>
</body>
</html>
desktopCaptuer.js
chrome.windows.getCurrent({}, w => {
chrome.windows.update(w.id, { focused: true }, () => {
document.getElementById("captuer").onclick = () => {
const sources = ["screen", "window", "tab"];
chrome.tabs.getCurrent((tab) => {
chrome.desktopCapture.chooseDesktopMedia(sources, tab, (streamId) => {
let track, canvas;
navigator.mediaDevices.getUserMedia({
video: {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: streamId
},
}
}).then((stream) => {
track = stream.getVideoTracks()[0];
const imageCapture = new ImageCapture(track);
return imageCapture.grabFrame();
}).then((bitmap) => {
track.stop();
canvas = document.createElement("canvas");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
let context = canvas.getContext("2d");
context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL();
}).then((url) => {
chrome.downloads.download({
filename: "screenshot.png",
url: url,
}, () => {
canvas.remove();
});
}).catch((err) => {
console.log(err);
alert("Could not take screenshot");
})
});
});
}
});
});