The chrome extension I am developing inserts content scripts and css onto every page of a website. However, the user may have a certain page or pages he or she does not want the extension to run on, so it would be great if I could set up the browser action as basically a toggle on / off.
What I'd like to do is something like this:
chrome.browserAction.onClicked.addListener(function(tab) {
//IF ENABLED THEN DISABLE
//IF DISABLED THEN ENABLE
}
Any help would be greatly appreciated!
Such API is not provided. But two possible workarounds exist:
I. You can use the "disabled" flag variable and update it from your background page.
Background page:
function disableExtension(disabled)
{
chrome.windows.getAll({populate : true}, function (window_list)
{
for (var i = 0; i < window_list.length; ++i)
{
var window = window_list[i];
for (var j = 0; j < window.tabs.length; ++j)
{
var tab = window.tabs[j];
if (checkContentScriptExists(tab))
{
chrome.tabs.executeScript(tab.id, {code : "disabled = " + disabled + ";"}, allTabs: true)
}
}
}
// No matching url found. Open it in the new tab
chrome.tabs.create({ url : url, selected: true });
});
}
And content script should check the condition before the run
if (!disabled) doSomething();
II. A controversial approach to save disable variable within background page content
Background page:
function disableExtension(disabled)
{
global.disabled = disabled;
}
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.msg == "getDisabled") {
sendResponse({disabled: global.disabled});
return true;
}
});
and the content script should query currently disabled status before execution
chrome.runtime.sendMessage({msg: "getDisabled"}, function(response) {
if (!response.disabled) doSomething();
});
Chrome extensions have APIs of their own. You can invoke them via content scripts. Not sure if you can invoke them via any third party JS. Refer this ink
Hope it helps.
Related
Is there a way to detect if multiple browser tabs are opened of the same application.
Let's say i have www.test.com and i open 4 tabs of this website.
Is there a way to detect that multiple tabs are opened in JavaScript?
You can use my sysend.js library, it use localStorage to send messages between open tabs/windows. All to do is this code:
sysend.on('notification', function() {
sysend.broadcast('multiple');
});
sysend.on('multiple', function() {
// this will fire n-1 times if there are n tabs open if n > 1
alert('multiple pages');
});
sysend.broadcast('notification');
JSFIDDLE
There is, but it's not guaranteed to always be correct:
window.onload = function() {
var applicationCount = localStorage.getItem("applicationCount");
if (!applicationCount) {
applicationCount = 0;
}
localStorage.setItem("applicationCount", ++applicationCount);
}
window.onbeforeunload = function(e) {
var applicationCount = localStorage.getItem("applicationCount");
if (!applicationCount) {
applicationCount = 1;
}
localStorage.setItem("applicationCount", --applicationCount);
};
It uses the localStorage which is shared among the tabs. But note that if the browser crashes, the value is still saved.
The code in buttonClicked() function and onTabCreate() function worked when the button is the BrowserAction icon.
But when I added a popup.html and created few buttons in it. When "Trello" button is clicked. I want the code in buttonClicked() to be executed, I put this code in eventPage.js.
The control is reaching the function, but I'm unable to capture the visible Tab now.
Why is it so? How can I fix this?
As evident from the code, imageData is holding the image of the tab (3rd line in buttonClicked())
The last 4th line in buttonClicked(), calls onTabCreate(). InonTabCreate(), I'm passing the imageData, but I'm not receiving the image.
I'll furnish more info if needed.
Thanks
buttonClicked()-
function buttonClicked() {
chrome.tabs.captureVisibleTab(null, {}, function (image) {
imageData = image;
createdTabUrl = chrome.extension.getURL('cardCreate.html');
chrome.tabs.query({ active: true }, function (tabs) {
var i, tab;
// Only select the current active tab, not any background tab or dev tools
for (i = 0; i < tabs.length; i += 1) {
// TODO: more robust way to check if current tab is a page from this extension (either when I get a static extension id or with a flag)
if (tabs[i].url.match(/^http/) || tabs[i].url.match(/^chrome-extension.*\/cardCreate\.html$/)) {
tab = tabs[i];
}
}
chrome.tabs.create({ url: createdTabUrl, index: (tab.index || 0) + 1 }, onTabCreated);
});
});
}
onTabCreate()-
// TODO: more robust way to send image data to page ?
function onTabCreated(tab) {
setTimeout(function (){
chrome.runtime.sendMessage({ imageData: imageData }, function(response) {
// Callback does nothing
});
}, 1000);
var views = chrome.extension.getViews();
for (var i = 0; i < views.length; i++) {
var view = views[i];
// If this view has the right URL and hasn't been used yet...
if (view.location.href == createdTabUrl) {
console.log("----------------------");
// console.log(view);
// console.log(view.location.href);
}
}
}
chrome.browserAction.onClicked.addListener(buttonClicked);
EDIT :
window.onload = function() {
document.getElementById('loginText').addEventListener('click', login);
document.getElementById('Trello').addEventListener('click', buttonClicked);
//listener for ButtonClick
// In popup.html , I included eventPage.js as well.
};
I am trying to display a 'mask' on my client while a file is dynamically generated server side. Seems like the recommend work around for this (since its not ajax) is to use an iframe and listen from the onload or done event to determine when the file has actually shipped to the client from the server.
here is my angular code:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
e.load(function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
});
angular.element('body').append(e);
This works great in Firefox but no luck in Chrome. I have also tried to use the onload function:
e.onload = function() { //unmask here }
But I did not have any luck there either.
Ideas?
Unfortunately it is not possible to use an iframe's onload event in Chrome if the content is an attachment. This answer may provide you with an idea of how you can work around it.
I hate this, but I couldn't find any other way than checking whether it is still loading or not except by checking at intervals.
var timer = setInterval(function () {
iframe = document.getElementById('iframedownload');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Check if loading is complete
if (iframeDoc.readyState == 'complete' || iframeDoc.readyState == 'interactive') {
loadingOff();
clearInterval(timer);
return;
}
}, 4000);
You can do it in another way:
In the main document:
function iframeLoaded() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
}
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element('body').append(e);
In the iframe document (this is, inside the html of the page referenced by url)
window.onload = function() {
parent.iframeLoaded();
}
This will work if the main page, and the page inside the iframe are in the same domain.
Actually, you can access the parent through:
window.parent
parent
//and, if the parent is the top-level document, and not inside another frame
top
window.top
It's safer to use window.parent since the variables parent and top could be overwritten (usually not intended).
you have to consider 2 points:
1- first of all, if your url has different domain name, it is not possible to do this except when you have access to the other domain to add the Access-Control-Allow-Origin: * header, to fix this go to this link.
2- but if it has the same domain or you have added Access-Control-Allow-Origin: * to the headers of your domain, you can do what you want like this:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element(document.body).append(e);
e[0].contentWindow.onload = function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
};
I have done this in all kinds of browsers.
I had problems with the iframe taking too long to load. The iframe registered as loaded while the request wasn't handled. I came up with the following solution:
JS
Function:
function iframeReloaded(iframe, callback) {
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframeReloaded(iframe[0], function () {
console.log('Reloaded');
})
JQuery
Function:
$.fn.iframeReloaded = function (callback) {
if (!this.is('iframe')) {
throw new Error('The element is not an iFrame, please provide the correct element');
}
let iframe = this[0];
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframe.iframeReloaded(function () {
console.log('Reloaded');
})
I've just noticed that Chrome is not always firing the load event for the main page so this could have an effect on iframes too as they are basically treated the same way.
Use Dev Tools or the Performance api to check if the load event is being fired at all.
I just checked http://ee.co.uk/ and if you open the console and enter window.performance.timing you'll find the entries for domComplete, loadEventStart and loadEventEnd are 0 - at least at this current time:)
Looks like there is a problem with Chrome here - I've checked it on 2 PCs using the latest version 31.0.1650.63.
Update: checked ee again and load event fired but not on subsequent reloads so this is intermittent and may possibly be related to loading errors on their site. But the load event should fire whatever.
This problem has occurred on 5 or 6 sites for me now in the last day since I noticed my own site monitoring occasionally failed. Only just pinpointed the cause to this. I need some beauty sleep then I'll investigate further when I'm more awake.
I try to make some changes on a Chrome extension. I need the extension checks the value and if it is true, then execute a script. If false, then do nothing. I wrote something like this:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(localStorage["statusOfSomething"]){
chrome.tabs.executeScript(tabId, {file: "file.js" ,runAt:'document_end'});
}
});
But this takes the initial value of localStorage["statusOfSomething"] always. So there is an async function to register a listener for "onUpdated". But I need to check the localStorage["statusOfSomething"] value of "now", not the value of the time by registering the listener.
How can I do this?
Edit:
Actually I was trying to check two things:
if the website is in the site list of extension
if this website is enabled for the extension
Now here the full story;
There are some websites, I defined them on background.js file. Let's say;
a.com, b.com ... etc.
var sites = [{
name : "a",
wildcard : ["*://a.com/*"],
js : "a.js"
},{
name : "b",
wildcard : ["*://b.com/*"],
js : "b.js"
}]
and there are statuses of the sites (enable/ disable; true/false)
I think it was good to store statuses by localStorage, so I write as initial value true:
for(var i = 0; i<sites.length; i++){
localStorage[sites[i].name] =true;
}
As option; it was needed to addListener for changes:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if(request.method == "setSite"){
var name = request.site;
var status = request.active;
localStorage[name] = status;
}
return true;
});
If user checks the checkbox for status option of the website, then options.js sends message:
$('#' + name).change(function(){
var status = $(this).is(':checked');
chrome.extension.sendMessage({method: "setSite", site: this.id, active: status}, function(response) {
console.log(response.data);
});
});
Now, back to background.js , for every update of the tab, I need to check these two things together:
1. am I interested in this website?
2. is it enable for me now?
So I wrote:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
var url = tab.url;
for(var i = 0; i<sites.length; i++){
var site = sites[i];
var name = site.name;
var wildcard = site.wildcard;
if(localStorage[name] && testUrl(url,wildcard)){
chrome.tabs.executeScript(tabId, {file: site['js'] ,runAt:'document_end'});
break;
}
}
}
});
Here, I get the value of localStorage[sitename] true
If you are more interested, you can see the code on github (the version)
I'm developing a chrome extension and i've met a very strange bug - my code works well on Mac OS, but doesn't work on Windows and Linux versions of Chrome. Versions are the same.
function captureAllScreen() {
chrome.windows.getCurrent(function(w) {
chrome.tabs.captureVisibleTab(w.id, {"format":"png"}, function(response) {
var image = response;
var url;
chrome.tabs.getSelected(w.id, function(response) {
url = response.url;
});
var viewTabUrl = [chrome.extension.getURL('app.html'),
'?id=', id++].join('');
chrome.tabs.create({url: viewTabUrl}, function(tab) {
var targetId = tab.id;
var addSnapshotImageToTab = function(tabId, changedProps, tab) {
if (tabId != targetId || changedProps.status != "complete") {
return;
};
chrome.tabs.onUpdated.removeListener(addSnapshotImageToTab);
var views = chrome.extension.getViews();
for (var i = 0; i < views.length; i++) {
var view = views[i];
if (view.location.href == viewTabUrl) {
view.twm_Draw.sendScreen(image, url); //Application-specific method
break;
}
}
window.close();
};
chrome.tabs.onUpdated.addListener(addSnapshotImageToTab);
});
});
});
};
Update:
What i want to do with this code - is to take a screenshot and tab url and send it to my extension's page. When user clicks on my extension's icon - it opens a popup with two buttons, one of it fires this function.
In Mac Os everything works - this code takes a screenshot, tab url, opens new tab with my application and sends the data there. On Linux & Windows versions of chrome it doesn't send the data, after clicking the icon in the popup you just get a blank tab opened.
I think this part might be causing problems:
var url;
chrome.tabs.getSelected(w.id, function(response) {
url = response.url;
});
//using url
The rest of the code should be wrapped into callback function, otherwise order of execution is not guaranteed.
I'm guess it's only supported on Mac, whatever it does:
view.twm_Draw.sendScreen(image, url); //Application-specific method
I don't know about Unix but on Windows you can only get a screenshot using a NPAPI plugin like the Google extension for screen capture.