activeTab permission issue for Chrome extension - google-chrome

I'm running the following code in a background.js script for my Chrome extension:
chrome.browserAction.onClicked.addListener(captureCurrentTab());
function handleCapture(stream) {
console.log('content captured');
console.log("backround.js stream: ", stream);
alert(stream);
// localStream = stream; // used by RTCPeerConnection addStream();
// initialize(); // start signalling and peer connection process
}
function captureCurrentTab() {
console.log('reqeusted current tab');
chrome.tabs.query({active : true}, function(tab) {
console.log('got current tab');
chrome.tabCapture.capture({
audio : true,
video : false
}, handleCapture);
});
}
However, this gives me the following error:
Unchecked runtime.lastError while running tabCapture.capture: Extension has not been invoked for the current page (see activeTab permission). Chrome pages cannot be captured.
However, I specifically am granting activeTab permission in manifest.json:
"permissions": [
"tabs",
"tabCapture",
"activeTab",
]
Thanks for your help!

When the activeTab permission is declared, you only get access to the current tab when a user performs certain actions that imply they want you to have access.
The following user gestures enable activeTab:
Executing a browser action
Executing a page action
Executing a context menu item
Executing a keyboard shortcut from the commands API
Accepting a suggestion from the omnibox API
The error is telling you that capture the current tab because the user hasn't performed one of the actions listed above.
It looks like you might already understand this and just have an error in your code. When registering captureCurrentTab as the click listener for the browser action, you are actually executing it immediately instead of passing the function by reference. Change your first line to this:
// Remove the () after captureCurrentTab
chrome.browserAction.onClicked.addListener(captureCurrentTab);

Chrome pages cannot be captured. means that you are trying to capture a chrome://, chrome-extension://, or similar Chrome specific page which is not allowed. Make sure the current page is http:// or https://.

Related

get microphone permission in unpackaged Chrome Extension

I am making a Chrome Extension in which the microphone keeps listening all the time in lifetime of a Chrome Window.
I am trying to include audioCapture in permissions in manifest.json,
But I get the error:
audioCapture' is only allowed for packaged apps, but this is a extension
What can I do in this?
Is there any other way through which mic keeps listening?
I guess you can use getUserMedia() in content js file or if you want to get permission in manifest.json, try packaging your app and then load it again
'audioCapture' permission isn't supported yet for Chrome extensions manifest (see chrome extension documentation for the complete list).
You can trigger it in your content js files or in popup.js calling getUserMedia promise like this:
console.log('try trigger authorization');
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then((mediaStream) => {
//in promise will be triggered user permission request
})
.catch((error) => {
//manage error
});
This workaround works fine for my purpose.

Modify local storage via popup, and use stored values in contentscript

I'm trying my hand at creating a chrome extension, but am running into a wall.
I want to be able to use the browser-action popup to write/modify values into local storage (extension storage).
Then, I want to use the stored values in a content script.
From what I've read, it looks like I need a background file? but I'm not sure.
Some coded examples would be extremely appreciated!
Thanks for your help!
You can avoid using a background page as a proxy if you use chrome.storage API. It's a storage solution that is available from Content Scripts directly.
Here is a comparison between it and localStorage in the context of Chrome extensions.
An important thing to note is that it's asynchronous, making code slightly more complicated than using localStorage:
/* ... */
chrome.storage.local.get('key', function(value){
// You can use value here
});
// But not here, as it will execute before the callback
/* ... */
But to be fair, if you go with the background being the proxy for data, message passing is still asynchronous.
One can argue that once the data is passed, localStorage works as a synchronous cache.
But that localStorage object is shared with the web page, which is insecure, and nobody stops you from having your own synchronous storage cache, initialized once with chrome.storage.local.get(null, /*...*/) and kept up to date with a chrome.storage.onChanged listener.
Background pages can access the localStorage variables saved by your extension. Your content script only has access to the localStorage of the website open in a specific tab. You will therefore need to send the variables from the background page to the content script. The content script can then access these variables.
The following code saves a localStorage variable in the background script and then sends it to the content script for use.
Since you requested a coded example, I've written you one. This project would have a background page and a content script. Using localStorage in your popup will allow the background page to access these variables for use in the content script.
Something like this:
background.js
// When a tab is updated
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
// When the tab has loaded
if(changeInfo.status == 'complete') {
// Query open tabs
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
// Get URL of current tab
var tabURL = tabs[0].url;
// If localStorage is not empty
if(localStorage.length != 0) {
// Set a local storage variable
localStorage.helloworld = "Hello World";
// Send message to content script
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// Send request to show the notification
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
});
});
}
});
}
});
contentscript.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// Use the local storage variable in some way
if(request.greeting == "hello") {
var hello = localStorage.helloworld;
// do something with the variable here
}
});
Once you have this working, consider switching to chrome.storage

Chromecast event for device disconnect (sender)

Is there event that will notify a sender app when the use selects "stop cast" from within the chrome extension?
I've a chrome sender app get's in a limbo state if the user chooses to stop the cast from the extension instead of the app cast button.
EDIT:
This is some relevant code:
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
this.currentMediaSession = mediaSession;
// ...
this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
// ...
};
CastPlayer.prototype.onMediaStatusUpdate = function (e) {
console.log(e);
};
Have you tried Session.addUpdateListener(listener) ? I think the listener will be notified when the session is no longer alive.
It seems Google developers are pretty aware of that! :D
They've just update their senders sample code with a commit that is exactly what seems you're looking for: Added session update listener to handle disconnect by clicking cast extension
There's also another commit with the same text in another sample but with less code, here you have: https://github.com/googlecast/CastHelloVideo-chrome/commit/776559c9aaf16d7d82c62ee4dea611b6177ac217

Access-Control-Allow-Origin error in a chrome extension

I have a chrome extension which monitors the browser in a special way, sending some data to a web-server. In the current configuration this is the localhost. So the content script contains a code like this:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data)...
xhr.open('GET', url, true);
xhr.send();
where url parameter is 'http://localhost/ctrl?params' (or http://127.0.0.1/ctrl?params - it doesn't matter).
Manifest-file contains all necessary permissions for cross-site requests.
The extension works fine on most sites, but on one site I get the error:
XMLHttpRequest cannot load http://localhost/ctrl?params. Origin http://www.thissite.com is not allowed by Access-Control-Allow-Origin.
I've tried several permissions which are proposed here (*://*/*, http://*/*, and <all_urls>), but no one helped to solve the problem.
So, the question is what can be wrong with this specific site (apparently there may be another sites with similar misbehaviour, and I'd like to know the nature of this), and how to fix the error?
(tl;dr: see two possible workarounds at the end of the answer)
This is the series of events that happens, which leads to the behavior that you see:
http://www.wix.com/ begins to load
It has a <script> tag that asynchronously loads the Facebook Connect script:
(function() {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
Once the HTML (but not resources, including the Facebook Connect script) of the wix.com page loads, the DOMContentLoaded event fires. Since your content script uses "run_at" : "document_end", it gets injected and run at this time.
Your content script runs the following code (as best as I can tell, it wants to do the bulk of its work after the load event fires):
window.onload = function() {
// code that eventually does the cross-origin XMLHttpRequest
};
The Facebook Connect script loads, and it has its own load event handler, which it adds with this snippet:
(function() {
var oldonload=window.onload;
window.onload=function(){
// Run new onload code
if(oldonload) {
if(typeof oldonload=='string') {
eval(oldonload);
} else {
oldonload();
}
}
};
})();
(this is the first key part) Since your script set the onload property, oldonload is your script's load handler.
Eventually, all resources are loaded, and the load event handler fires.
Facebook Connect's load handler is run, which run its own code, and then invokes oldonload. (this is the second key part) Since the page is invoking your load handler, it's not running it in your script's isolated world, but in the page's "main world". Only the script's isolated world has cross-origin XMLHttpRequest access, so the request fails.
To see a simplified test case of this, see this page (which mimics http://www.wix.com), which loads this script (which mimics Facebook Connect). I've also put up simplified versions of the content script and extension manifest.
The fact that your load handler ends up running in the "main world" is most likely a manifestation of Chrome bug 87520 (the bug has security implications, so you might not be able to see it).
There are two ways to work around this:
Instead of using "run_at" : "document_end" and a load event handler, you can use the default running time (document_idle, after the document loads) and just have your code run inline.
Instead of adding your load event handler by setting the window.onload property, use window.addEventListener('load', func). That way your event handler will not be visible to the Facebook Connect, so it'll get run in the content script's isolated world.
The access control origin issue you're seeing is likely manifest in the headers for the response (out of your control), rather than the request (under your control).
Access-Control-Allow-Origin is a policy for CORS, set in the header. Using PHP, for example, you use a set of headers like the following to enable CORS:
header('Access-Control-Allow-Origin: http://blah.com');
header('Access-Control-Allow-Credentials: true' );
header('Access-Control-Allow-Headers: Content-Type, Content-Disposition, attachment');
If sounds like that if the server is setting a specific origin in this header, then your Chrome extension is following the directive to allow cross-domain (POST?) requests from only that domain.

chrome.management.onEnabled Not Firing

I have this in my background.html:
chrome.management.onEnabled.addListener(function(ExtensionInfo info) {
alert('123');
});
which gives me an error: Uncaught SyntaxError: Unexpected identifier
If I remove info from function(ExtensionInfo info), I don't get any errors, but it's not firing the alert. Where did I go wrong?
Also, I added "management" inside permissions in manifest.json, so that's not the problem.
You won't be able to catch chrome.management.onEnabled event for your own extension.
If you are trying to execute some code on first extension installation then you would need to store some flag in a local storage.
background.html
if(!localStorage["first_run"]) {
//do something at first run here
localStorage["first_run"] = "done";
}
(for more advanced solution see this answer)
If you want to execute some code each time extension starts (browser startup) just put in into background.html.