I have a listener defined in my content script in chrome extension:
document.addEventListener("startRecording", function(data) {
chrome.runtime.sendMessage({'action' : 'captureCurrentTab'});
});
and have a function defined in my extension.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.action == "captureCurrentTab"){
captureCurrentTab();
}
});
function captureCurrentTab() {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabCapture.capture(MediaStreamConstraint, handleCapture);
});
}
var MediaStreamConstraint = {
//audio: true,
video: true,
videoConstraints: {
mandatory: {
chromeMediaSource: 'tab',
minWidth: 1920,
maxWidth: 1920,
minHeight: 1080,
maxHeight: 1080
}
}
};
function handleCapture(stream) {
console.log('content captured');
console.log("Adding Stream: ", stream);
}
but when i send message to start recording from my web application like this:
var event = document.createEvent('Event');
event.initEvent('startRecording');
document.dispatchEvent(event);
then extension throws exceptions:
1) Error in response to tabCapture.capture: MediaStream is mandatory.
2) Unchecked runtime.lastError while running tabCapture.capture: Extension has not been invoked for the current page (see activeTab permission). Chrome pages cannot be captured.
Here are the permissions i have supplied:
"permissions": [
"tabCapture",
"tabs",
"activeTab",
"http://*/*",
"https://*/*" ,
"http://localhost:1615/*"
]
But when I click on my extension button and repeat the same process (send message for recording) then everything works fine. I don't know why I have to click extension button every time to start capturing screen.
How can I start it automatically?
I have also defined shortcut keys for my extension. When I press them before sending message for recording then everything works fine. but when I triggered/simulate them from my application then again end up with same exception.
Please help.
Problem here is that Chrome loses track of the fact that this event was initiated with a click. It is therefore not "invoked".
You should instead bind a click event listener in your content script code/context, and send the message from there directly. I think it should be enough for Chrome to accept that as invocation by the user.
Alternatively, direct sending of a message to the extension bypassing the content script may work. This is achievable with "externally_connectable" method (docs).
Related
I have a popup.js:
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// dd click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript()
// document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript);
Which I use to inject my script into the page with the "start-btn" inside my popup.html.
This is my main.js which includes my functions I would like to call on a page:
function pong() {
// do something
}
function ping() {
// do something else
}
my manifest.json:
{
"manifest_version": 2,
"name": "test app",
"description": "test desc",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["main.js"]
}
]
}
So basically my setup is that I have a popup.html which includes 3 buttons and they should call one of the functions inside my main.js dpending on what button i press.
But I can not get that working. Currently I only can make at least one function call if I directly call pong() inside main.js on load. But I would need to call one of the functions after I click on a button inside my popup.html.
EDIT: I have updated the code as far as I understood. I am very sorry but I don't understand what would be needed to be changed else to fulfill your proposal. I mean how to write it to be more correct.
EDIT 2: I have removed the line chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' }); as well as document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript)and added injectStartSearchScript()into the popup.js file. Is that what you meant?
Updated and complete example with explanation (you are almost there!)
manifest
You manifest looks good, no changes needed there.
This configuration says to load the content script in each tab. So before popup even opens each tab will have "main.js" injected into it (exactly once).
popup.js
Popup script looks good, no changes needed there.
The tab lookup is still necessary, since to send a message to a specific tab, must know its id. The popup looks for the currently active tab in current window (same as the popup is in) and sets up the button click actions to send a message to the tab.
main.js
Will need minor changes here
Make sure to register the onMessage listener, as included in the example below.
note the conditions: message.startSearch and message.startDeals -- these must match the messages sent from the popup, i.e. when popup sends a message with content {startDeals: true}, the if condition is startDeals. It is matching by a key in the sent message and if the key does not match any condition, the message is going to be ignored.
function pong() {
// do something
alert('Start!');
}
function ping() {
// do something else
alert('Deals!');
}
// register listener to receive messages
chrome.runtime.onMessage.addListener(message => {
// what to do on each received message:
if (message.startSearch) pong();
else if (message.startDeals) ping();
});
// sanity check: content has loaded in the tab
console.log('content loaded');
One more note as it relates to debugging extensions (and perhaps a source of some of these debugging issues) when the content script has been configured to be injected in the manifest, Chrome will inject main.js into tabs, in a separate extension context. If, after this injection, the developer reloads the extension (circular arrow in chrome://extensions or other tool), this will invalidate the context of the content script in the tab. The tab has to be reloaded to reactivate the content script. This is not an issue with a real user, but it does happen during debugging, so double check this is not the cause of issues while debugging.
Sorry for my poor English, i hope you can understand the issue.
I'm new to chrome extension development,and for sure in my code there are a lot of
thing to change or optimize;
anyway i've written a simple code that, (seems) works at least from my chrome.
The code clicks a button every X minutes in specific page, then wait and parse the result in page.
I've :
a content script (loaded from manifest.json) which "inject" some button and text Input box in page, so user can sets some "filter params" before click a "start button"; the start button then sendMessage() to background.js to set Alarm Event for the click ;
an eventPage (which is set persistent true in actually ) which handle the request from tabs and set a countdown alarm for each tab; when X min are passed fire a message to the interested tab;
I also have a popup.html e popup.js not important here (i think).
I've to distribuite this extension manually, so i would distribuite a zip that user can load with "developer mode ".
*Now the issue is: why the code was working only on my Chrome ? *
I've tested with others 2-3 laptop with Chrome, the background script is loaded (i can see the background page printint console log)
but in webpage the contents.js seems no way executed .
In my chrome works well: i can see in console some initial output (i print the name of dir extension to check) and
the dynamic created element (button,input box ect.) in page.
And all is working, i can fire the start button and receive results of parsing.
During the development i've never run the extension on other machine. Yesterday i've succssfully tested on 2-3 laptop.. then i made only few change but nothing serious.
Today i can run only in my chrome.
In other pc nothing, neither the simple console.log output first line of script.
I can read in console log :
"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
but this also in my working istance in my laptop chrome .
The zip file is the same and the extraction is good, in fact i can actually load the extension and i see the background page debug console.log() sentences
In some case, in laptop where it dosen't work, i've received a message relative jQuery and the fact that chrome.runtime.sendMessage() is not defined; and it points to code in webpage, not mine.
I've see that in webpage code there is something like:
var extid = "mcmhdskbnejjjdjsdkksmeadjaibo";
var extVer = "1.5";
var extStatus = 0;
$(document).ready(function () {
///...
chrome.runtime.sendMessage(extid, {message: "version"},
function (reply) {
if (reply) {
if (reply.version) {
if (reply.version == extVer) {
if (reply.gminfo != 'OK') {
extStatus = 1; /// ...
Seems that chrome.runtime is undefined, and the webpage can't call the sendMessage().
EDIT: this undefined occurs only when my extension is loaded
Maybe there is some conflict when i load my extension? But in my chrome browser works...
Can some expert indicate in where direction i've to investigate?
Thanks a lot for any suggestions.
My Manifest.json :
{"manifest_version": 2,
"name": "myAlarm",
"description": "This extension alerts.",
"version": "0.1",
"permissions": [
"alarms",
"system.cpu",
"storage",
"tabs",
"webNavigation",
"https://www.mytargetsite.com/subUrl/"
],
"web_accessible_resources": [
"icon.png",
"vanillaSelectBox.css"],
"content_scripts": [
{
"matches": ["https://www.mytargetsite.com/subUrl/"],
"css": ["vanillaSelectBox.css"],
"js": ["jquery-3.3.1.min.js","vanillaSelectBox.js","taffy-min.js","content.js"],
"run_at": "document_end"
}
],
"background": {
"scripts": ["eventPage.js"],
"persistent": true
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
....
}
}
My contents,js (stripped):
chrome.runtime.onMessage.addListener(
function(request, sender) {
// here i parse message "time'up" from background js
});
window.addEventListener('load', function() {
var pt=chrome.runtime.getURL('filterOff.wav');
var p=pt.split("/");
console.log("[myAlarm v0.1] started" );
console.log("[myAlarm v0.1] folder : ("+p[2]+")");
// here i start an active wait for the presence in page of button with ID= btntarget_id
waitForElementToDisplay("#btntarget_id", 500); //when function find button then create and add button and input text to webpage
});
My eventPage.js :
var curr_alarms =[];
chrome.extension.onMessage.addListener(function(request, sender)
{ /// here receive start countdown message from content.js and set alarm ...
}
chrome.alarms.onAlarm.addListener(function(alarm) {
/// here i manage each alarm for each tab
});
chrome.tabs.onRemoved.addListener(function(tabid, removed) {
// ...
});
chrome.tabs.onUpdated.addListener(function
(tabId, changeInfo, tab) {
//
});
edit : in browser where it dosen't work i can read also :
Access to XMLHttpRequest at 'https://mytargetsite.com/suburl/grid.php' (redirected from 'https://mytargetsite.com/suburl/grid.php') from origin 'https://mytargetsite.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
The fact that the declared content script runs or not, should be verified by inspecting in devtools => sources sub-tab => content scripts sub-sub-tab. If it really doesn't run, there can be just two explanations: the URL is different (for example not https) or extensions are blocked by their domain admin via runtime_blocked_hosts which you can see in chrome://policy.
Your development mode extension's id will be different on a different machine unless you pin it by adding a "key" in manifest.json
To use chrome.runtime to send messages to your extension from a webpage code (not from a content script!) your extension's manifest should declare "externally_connectable" and use a different event onMessageExternal, see also sending messages from web pages.
The CORS error may be irrelevant to your code (you can investigate the source of the error by expanding the error's call stack in devtools console).
I want to be able to communicate from content-script (any tab) to background.
According to the documentation, I should be using chrome.extension chrome.runtime.onMessage with chrome.runtime.sendMessage.
So that's what I did:
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"*://*/*"
],
background.js
console.info('1');
chrome.runtime.onMessage.addListener((request, sender, sendReponse) => {
console.info('2');
sendReponse({ msg: 'foo' });
return true;
});
I'm not very sure the return true; is needed. I tried with and without.
After building and reloading the extension I accessed chrome background page through chrome://extensions
> 1 // background.js file was run
> chrome.runtime.sendMessage({ hello: 'so' }, v => console.info(v))
> undefined
> undefined // This is the callback response I get. Why?
More important, I also get an empty response callback when running sendMessage from other tabs (i.e. stackoverflow.com)
> chrome.runtime.sendMessage('extension id', { hello: 'so' }, v => console.info(v))
> undefined
> undefined
Why do I get an empty response callback?
Am I missing any special permission? Wrong parameters? Maybe wrong API functions to do so?
runtime.sendMessage doesn't send the message to the sender's context since Chrome 49.
If sending to your extension, the runtime.onMessage event will be fired in every frame of your extension (except for the sender's frame),
webpages can send messages to your background/event page in two cases:
externally_connectable key in manifest.json allowed that page
a content script of your extension is loaded for that page, in which case you can switch the context to your extension in the console toolbar to be able to send messages manually from console:
The content script can be declared in manifest.json in which case it's autoinjected by [re]loading the page. Or you can inject it explicitly with tabs.executeScript from the background/event page.
I am building a chrome extension that should get notified every time a new tab has opened and load page, and for that purpose I'am using chrome.tabs.onUpdated event.
The problem is that in case an iframe is inserted on that page/tab that is hosted on some domain(has src), onUpdated event is trigered. Is there a way to differentiate these events for "real" tab load from those triggered for iframe load?
This question helped me with my extension, recognizing the difference between a new page and loading content on the same page, so thought I'd share my solution. First you need to call onUpdated in background.js:
Manifest
{
"name": "My test extension",
"version": "1",
"manifest_version": 2,
"background": {
"scripts":["background.js"]
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["contentscript.js"]
}
],
"permissions": [
"tabs"
]
}
background.js
chrome.tabs.onUpdated.addListener(
function(tabId, changeInfo, tab) {
// read changeInfo data
if (changeInfo.url) {
// url has changed; do something here
}
}
);
Then you can expand that script to send message data from background.js to your content script (using chrome.runtime.sendMessage):
background.js (con't)
chrome.tabs.onUpdated.addListener(
function(tabId, changeInfo, tab) {
// read changeInfo data
if (changeInfo.url) {
// url has changed; do something here
// like send message to content script
chrome.tabs.sendMessage( tabId, {
message: 'hello!',
url: changeInfo.url
})
}
}
);
And finally listen for that data in your extension's content script to be used however:
contentscript.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// listen for messages sent from background.js
if (request.message === 'hello!') {
console.log(request.url) // new url is now in content scripts!
}
});
You can pass whatever data you'd like from background.js. Hope that helps someone! \ (•◡•) /
tabs.onUpdated triggers when state changes between loading to complete. Presumably, inserting an iframe puts the tab to loading state again.
You could see if details.url is defined in onUpdated listener - if not, you know that the document's URL did not change.
Perhaps you should use webNavigation API instead for your purpose. There, you get a TransitionQualifier that you can use to filter out subframe navigation.
i'm trying sending data from website javascript to my chrome extension.
I read about sending msgs from website and i couldn't make it work.
in my website:
$("#button-id").click(function(){
chrome.runtime.sendMessage("msg", {arg: "1"},
function(response){alert("got response: " + response);});
});
and in background.js (in the chrome extension)
chrome.runtime.onMessageExternal.addListener(
function() {
alert("in background");
return "1";
});
I also added to the manifest:
"externally_connectable": {
"matches": ["*://localhost/project/public/*","http://localhost/project/public/*","*://*/*/*"]
},
and all I'm getting when I'm clicking that button is alert that says "got response undefined".
something that i tried that maybe will help to figure it out:
when I'm on my website, opening chrome developer console, typing "chrome.runtime.sendMessage("jjj");"
I'm getting 'undefined'.
thanks in advance!!!
To send a response, you need to pass it to sendResponse parameter of the listener, which is the third parameter:
chrome.runtime.onMessageExternal.addListener(
function(message, sender, sendResponse) {
alert("in background");
sendResponse("1");
}
);
One thing to note: if sendResponse is going to be called asynchronously, you have to return true;
Also, when sending a message from a webpage, you have to supply the extension's id as the first parameter. In your example code it's "msg": it must be an extension id.