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");
})
});
});
}
});
});
Works for me, using Chromium 107.0.5304.121 (Official. Build) Arch Linux (64-Bit).
Go to https://stackoverflow.com/
Click on the extension icon.
A new window opens, with the text "Select what you want to share. Screenshots wants to share the contents of your screen with stackoverflow.com"
Click on one of the tabs: Entire Screen, Window, Chromium Tab
Click on a screenshot preview or tab title
Click "Share"
The browser displays an alert with the text "Screenshot saved", and a file named "Screenshot.png" is created in the default downloads directory.
So, #Norio Yamamoto 's solution suits me perfectly, because I then need to make a popup to give a name and do other processing on my screen, so thanks to your help, I'm already moving on by starting to understand it HTML popups on extensions! Thanks !
For the problem itself, I was able to "fix" it in the end by reinstalling chrome, and it works as #Thomas Muller tells me... not sure why, maybe I had to break something with many tests, so the app was already working
But I noticed a problem on the version of the tutorial compared to the one with popup, the tutorial version does not work on: non-reload pages (thanks #wOxxOm for the tip by the way), nor on chrome home pages, nor on the extension page, so I really prefer the popup version, but I need to dig more to improve that
Thanks again !
I am using Cypress with Cucumber.
I am trying to test cross origin login but the origin method keeps on throwing error:
Code:
Given(/^the user login to the Test Page$/, function () {
cy.visit("https://example-originalURL");
cy.get("button").contains("Login").click();
const credentials = {
username: "hello",
password: "user",
};
cy.origin("https://example-newURL", { args: credentials }, ({ username, password }) => {
cy.get("#email", { timeout: 20000 }).type(username);
cy.get("#password").type(password, { log: false });
cy.get("button").contains("Login").click();
});
});
Cypress.config.js
module.exports = defineConfig({
projectId: "t7unhv",
e2e: {
setupNodeEvents(on, config) {
on("file:preprocessor", cucumber());
on('task', {
log(message) {
console.log(message +'\n\n');
return null;
},
});
},
specPattern: "./cypress/e2e/features/*.feature",
chromeWebSecurity: false,
experimentalSessionAndOrigin: true,
defaultCommandTimeout: 15000,
env: {
devCentralUrl: "https://***.dev.***.com.au/login",
testCentralUrl:
"https://***.test.***.com.au/login",
test***: "http://***.test.***.com.au",
dev***: "http://***.dev.***.com.au",
uat***: "https://***.uat.***.com.au",
dataSource: "",
environs: "test",
},
retries: {
runMode: 0,
},
pageLoadTimeout: 15000,
reporter: "mochawesome",
reporterOptions: {
reporterEnabled: "mochawesome",
mochawesomeReporterOptions: {
reportDir: "cypress/reports/mocha",
quite: true,
charts: true,
overwrite: false,
html: false,
json: true,
},
},
},
});
Error:
The following error originated from your test code, not from Cypress.
> on only accepts instances of Function
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
I have tried multiple syntax changes like not passing the credentials as optional argument to cy.origin.
If someone can provide a quick help, that will be great.
If the problem is in the test code, it is likely to be that newURL is undefined. The error message suggests the problem is in the app, but that might be a red herring.
Try just adding a fixed string for the cy.origin() key,
cy.origin('login', { args: credentials }, ({ username, password }) => {
...
})
I am using fine uploader to upload files to the Server but got an issue.
file gets uploaded to the Server perfectly fine but Server returns the response without { success : true } message, so, by default, fine Uploader treats it as failed upload and shows me error.
Is there a way to avoid this behavior?
How can I make fine uploader treat every response as a successful response even if the response does not have { "success": true }
here is my fine uploader code
this.manualUploader = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger'),
template: 'qq-template-manual-trigger',
request: {
endpoint: 'some end point'
},
thumbnails: {
placeholders: {
waitingPath: '../scripts/plugins/fine-uploader/placeholders/waiting-generic.png',
notAvailablePath: '../scripts/plugins/fine-uploader/placeholders/not_available-generic.png'
}
},
autoUpload: true,
debug: false,
callbacks: {
onComplete: function (event, id, xhr) {
** will call some functions here **
},
onError: function (id, name, errorReason, xhrOrXdr) {
try {
if(xhr.status == 204 && xhr.responseText.length == 0){
response = qq.parseJson('{"success": true}');
}
else{
response = qq.parseJson(xhr.responseText);
}
}
catch (exception){
}
}
},
failedUploadTextDisplay: {
mode: 'custom',
maxChars: 20,
responseProperty: 'error',
enableTooltip: true
}
});
I am trying to download some image files and store it for offline accessibility purpose of the app using Ionic framework. I have used two Cordova plugins named "Cordova-plugin-file" and "Cordova-plugin-file transfer". My code works on Android but faces a strange issue in iOS platform.
Error in Success callbackId: FileTransfer552364304 : TypeError: null
is not an object (evaluating 'result.lengthComputable'),
callbackFromNativecordova.js
Sometimes the code works, sometimes it throws me this error. Also I cannot access the error from my javascript code. Can anyone help? The code snippet is given below:
downloadImage: function(url, fileName) {
var deferred = $q.defer();
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
fs.root.getDirectory(
LOCAL_STORAGE_KEYS.app, {
create: true
},
function(dirEntry) {
// console.log(arguments);
dirEntry.getFile(
fileName, {
create: true,
exclusive: false
},
function(fe) {
console.log(arguments);
var p = fe.toURL();
console.log("In service the url path:", p);
fe.remove();
var ft = new FileTransfer();
console.log('File Transfer instance:',ft);
ft.download(
encodeURI(url),
p,
function(entry) {
console.log('In service the entry callback:',entry);
if (entry && entry.toURL) {
deferred.resolve(entry.toURL());
} else {
deferred.resolve();
}
},
function(err) {
console.log('Getting rejected:',err);
deferred.reject(err);
},
false,
null
);
},
function() {
deferred.reject(new Error('get file failed'));
}
);
}
);
},
function() {
deferred.reject(new Error('get directory failed'));
});
return deferred.promise;
}
I am using Chrome Version 35.0.1916.114 m
When I run my html page using IIS (e.g., localhost/test.html) and hit "Allow" to the http://localhost/ wants to use your microphone prompt, getUserMedia() fails with the following error:
NavigatorUserMediaError {constraintName: "", message: "", name: "TrackStartError"}
Code:
var constraints = {audio: true, video: false};
...
function successCallback(stream) {
...
}
function errorCallback(error){
console.log("navigator.getUserMedia error: ", error);
}
navigator.getUserMedia(constraints, successCallback, errorCallback);
What could be the cause of this error?
var mediaConstraints =
{
'mandatory' :
{
'OfferToReceiveAudio' : true,
'OfferToReceiveVideo' : false
}
};
declare this in your code before using constraints.