Chrome Extension's browser action with a popup that inject/delete CSS - google-chrome-extension

I would like to ask a way to inject css or delete injected css through browse action pop up window for the chrome extension. I had try to look through few places to get ideal on how to do it but I fail to understand them.
I would like to make extension which similar to "A browser action with a popup that changes the page color" but click on the div in the popup.html to load or unload the css file that created.
This is my current work (https://github.com/Zhekoay/Self-Custom-Dark-Theme) which direct insert css using content script. Now i would like to make it able to load or unload differently instead one-time load all.

Chrome API can't remove CSS injected via manifest.json.
Inject the code just like the demo extension does, but use a file parameter with a name of your content script that will add or remove (if it exists) a link element under document.documentElement with an id equal to chrome.runtime.id and href pointing to a web accessible CSS file.
remove "content_scripts" from manifest.json
add "web_accessible_resources": ["*.css"] to manifest.json
add a click handler for the div in popup.js
in the click handler: chrome.tabs.executeScript({file: 'content.js'});
content.js:
(function() {
var styleElement = document.getElementById(chrome.runtime.id);
if (styleElement) {
styleElement.remove();
return;
}
var css = ({
'www.youtube.com': 'app_yt_HoveredImg.css',
'www.messenger.com': 'fb_messenger_styles.css',
'www.google.com': 'google_styles.css',
'translate.google.com': 'google_styles.css',
'apis.google.com': 'google_styles.css',
})[location.hostname];
if (!css) {
return;
}
styleElement = document.createElement('link');
styleElement.id = chrome.runtime.id;
styleElement.rel = 'stylesheet';
styleElement.href = chrome.runtime.getURL(css);
document.documentElement.appendChild(styleElement);
})();
Note, in this workflow you only need "permissions": ["activeTab"] instead of "tabs": the advantage is that activeTab doesn't ask for permissions when the extension is installed.

Related

Chrome extension: add HTML into web page

I've seen this answer:
Chrome extension: Insert fixed div as UI
which inserts a div into the current web page.
However, it gets pretty tiresome to be doing this all the time:
"div.id = 'myDivId';" +
"div.style.position = 'fixed';" +
"div.style.top = '50%';" +
"div.style.left = '50%';" +
Is it possible to insert a fully-formed HTML template instead (i.e. that you include as template.html in the extension)?
There are several methods.
Expose resources to content script via web_accessible_resources
Then the content script can use them almost directly (by using chrome.runtime.getURL) in images, videos, iframes, or read the contents of html files using fetch() and XMLHttpRequest.
If your UI is complex then go with the iframe approach - its contents will be a full-fledged extension environment like a browser_action popup with its own chrome://extension URL.
The downside is that your resources may be read by any site directly and the sites may easily detect the presence of your extension.
Use messaging and read the resources via the background script
content.js:
chrome.runtime.sendMessage({cmd: 'getTemplate'}, html => {
document.body.insertAdjacentHTML('beforeend', html);
});
background.js:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.cmd === 'getTemplate') {
fetch('/templates/foo.html').then(r => r.text()).then(sendResponse);
return true;
}
});
Use you compiler/bundler
There are plugins for major bundlers/compilers that allow to inline the file contents in another one so you can include a HTML template in your compiled content script as a literal string.

Run content script in scope of a single tab

I'm developing an extension for Google Chrome. I have created an icon for my extension. What I'd like to do, is the following:
A user clicks extension's icon.
A new tab is opened.
Content scripts are run as set in manifest file, but only in this
tab.
I think it's clear, but e.g.: let's say I set in my manifest file a following property:
"content_scripts" : [
{
"matches" : ["http://stackoverflow.com"],
"js" : ["script.js"]
}
]
And let's say I have 2 tabs: the first one was opened manually, and the second one was opened by clicking extension's icon. Now, I open the stackoverflow's index page in both tabs. In the first tab nothing happens, but in the second one the "script.js" script is run.
How can I achieve it? I know I can implement a tab's id checking, but isn't there any simpler way to achieve it?
You shouldn't use the content_scripts entry in the manifest in this case. You can use chrome.tabs.executeScript calls to run the content scripts. You will need to track which tabs you created but that's not hard.
You can have something like this in a background script:
var createdTabs = {};
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.create( {}, function( tab ) {
createdTabs[tab.id] = true;
}
}
chrome.tabs.onUpdate.addListener(function(tabId, changeInfo) {
if( createdTabs[tabId] && changeInfo.url )
chrome.tabs.executeScript( tabId, {file: 'script.js'} );
}

Chrome Extension: access separate html documents from contentscript

I have a Chrome extension that loads/injects a contentscript.js This script appends html and css to the webpage.
Currently I have the html and css written into my contentscript. What I would like is for the css itself, as well as the body of my new elements to be in separate document, mostly so it looks better than having html and css as text in a .js document.
Then in the contentscript I would do something like
node = the_html_doc.html
document.getElementsByTagName("body")[0].appendChild(node);
But how do I access such a separate document from my contentscript? It needs to be available in all tabs (I use the activeTab permission), not just the url in the manifest "matches".
A better solution would be to use a background script. What I did was to make a file called background.html that would store nothing but templates. I then had my background script (background.js) setup to communicate with my content script (content.js). The content script would send a message to the background script with a command indicating it wants a template. Leveraging jQuery, i can easily select and return a template to my content script which can then be injected into the page.
Here is the code (bits an pieces):
background.html
<div id="template-1"></div>
<div id="template-2"></div>
...
background.js
chrome.runtime.onMessage.addListener(function(cmd, sender, sendResponse){
c = JSON.parse(cmd);
if(c.cmd == "GET_TEMPLATE"){
//respond with the template referenced by c.selector
sendResponse($(c.selector).outerHTML);
}
});
content.js
var command = {cmd:"GET_TEMPLATE", selector:"#template-1"};
chrome.runtime.sendMessage(JSON.stringify(command), function(response) {
//and here you should get your template
console.log(response);
//you can start using jQuery like $(response) to alter it
});
This method has worked flawlessly for me. I not only use commands here but I use them everywhere now, it works well with message passing.
You might be able to use the web_accessible_resources manifest setting, then in your content script you can just inject a link element that points to the chrome.extension.getURL(<filename>) value for the CSS, and inject a script element of type text/html with an id, and then fetch the contents of that node and use those for your appendChild call.

chrome.extension.onMessage.addListener Cannot read property 'onMessage' of undefined

In the background.html:
chrome.tabs.query({active:true, currentWindow:true},function(tabs){
chrome.tabs.sendMessage(tabs[0].id,"target.js");
});
In the content.js:
chrome.extension.onMessage.addListener(function(msg,sender,sendResponse){
if (msg == "target.js"){
extensionID = sender.id;
}
});
However, it doesn't work;
Uncaught TypeError: Cannot read property 'onMessage' of undefined
How to make it right?
You said "content.js is a content script and it is injected to the current tab by another content script.".
Herein lies your problem. Injected scripts are executed in the context of a page (as if they were created by the page itself), and therefore they have no access to any of the Chrome extension APIs.
You could use a custom event or postMessage to communicate between the page and your content script, which in turn communicates with the background page.
For instance, see this basic example:
// Injected script script
addEventListener('message', function(event) {
if (event.data && event.data.extensionMessage) {
alert(event.data.extensionMessage);
}
});
// Content script which injects the script:
chrome.extension.onMessage.addListener(function(message) {
postMessage({extensionMessage: message}, '*');
});
I think that you want to use content.js as a real content script though, rather than an injected script. If you want to know the differences between injected scripts and content scripts, see Chrome extension code vs Content scripts vs Injected scripts.
It looks like you're trying to do some action to the current tab. Are you sure you need a message to do that? I'm assuming that background.html is your extension's background page and content.js is your content script. Now I skip the background.html page and simply run a javascript file for my background page. My manifest.json page would look something like this.
"background": {
"scripts": [ "js/background.js"]
},
In that page, I add message listeners to my background page since that's the only way I know to interact with the background.js file. In your case however, it's the background page that doing the action therefore you could use the chrome.tab method to modify the current tab directly. Instead of onMessage, you may try something executeScript where the InjectDetails is the javascript or whatever you want to execute.
chrome.tabs.query({active:true, currentWindow:true},function(tabs){
chrome.tabs.executeScript(tabs[0].id, InjectDetails);
});
See more on that HERE.

Chrome Extension that copies image URL on click

I'm brand new to making Chrome Extensions and have done the simple tutorials, but I'm having trouble finding what I need. I want the extension to allow a user to chose an image on a webpage, and then copy the URL for that image into the extension. Can anyone help me out? I'm sure if I see an example I'd get a better grasp on how extensions can interact with a page.
From what I understand of your question, I'd say you want to create a context menu item that shows up when you right-click an image. For example, in your background script, use:
chrome.contextMenus.create({
title: "Use URL of image somehow",
contexts:["image"],
onclick: function(info) {
handleImageURL(info.srcUrl);
}
});
function handleImageURL(url) {
// now do something with the URL string in the background page
}
This will add a context menu item that shows up on all pages, but only when you right-click on images. When the user selects it, the onclick handler of the menu item fires handleImageURL with the URL of the image as the argument. The URL can be processed in any way you like, e.g., saved in a localStorage list, sent to a server via Ajax, or passed in a message to a listening content script in the current tab.
EDIT with alternative:
You might want a content script that gets injected into every page. The script could bind an event listener to every image element at load time:
// in my_content_script.js...
var imgs = document.getElementsByTagName("img");
for(var i = 0, i < imgs.length; ++i) {
imgs[i].addEventListener("click", function() {
alert(this.src);
// do things with the image URL, this.src
});
}
To inject it into all subdomains of example.com, your manifest would include:
...
"content_scripts": {
"matches":["*://*.example.com/*"],
"scripts":["my_content_script.js"]
},
...
Note that this pure-JS solution doesn't attach listeners to images dynamically added after load time. To do that in your content script with jQuery, use:
$(document).on("click", " img", function() {
alert(this.src);
});
And add your jQuery file name to the scripts array in your manifest, next to my_content_script.js.
Based on this Google Chrome Extension sample:
var images = [].slice.apply(document.getElementsByTagName('img'));
var imageURLs = images.map(function(image) {
return image.src;
});
chrome.extension.sendRequest(images);
For a more detailed example (e.g. how to handle the request), you can check out this extension I wrote called Image Downloader

Resources