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.
Related
I am trying to create chrome extension that will scrap data from my webpage and then will display it in browser action window. I wanted to use background page for this, cause if i understand extensions correctly, it is only element capable of non-stop working, without need of visible tab.
The problem is, the script i wrote for background.js doesn't work properly, when i use background.js:
var location = window.location.href = 'http://localhost/index.php';
console.log(location);
manifest.json:
"background": {
"scripts": ["src/background/background.js"]
},
The answer i get is chrome-extension://some_random_text/_generated_background_page.html.
It is possible to use background pages to navigate to my webpage, then fill some forms and scrap data for later use?
This is an old question, but I recently wanted to do exactly the same.
So I'll provide an answer for others who are interested.
Setting window.location still does not work in Chrome52.
There is a workaround though. You can first fetch the web page with fetch(), and then use document.write to set the content.
This works fine, and you can then query the document and do everything you want with it.
Here is an example. (Note that I'm using the fetch API, arrow functions and LET, which all work fine now in Chrome52).
fetch("http://cnn.com").then((resp) => {
return resp.text();
}).then((html) => {
document.open("text/html");
document.write(html);
document.close();
// IMPORTANT: need to use setTimeout because chrome takes a little
// while to update the document.
setTimeout(function() {
let allLinks = document.querySelectorAll('a');
// Do something with the links.
}, 250);
});
A chrome extension has two main parts, the extension process and the browser itself. The Background Page works on the extension process. It does not have direct access and information about your webpages.
To have scripts working non-stop on your webpages, you will need to use Content Scripts.
You can then communicate between your Content Script and your Background Page using messages
contentScript.js
var location = window.location.href = 'http://localhost/index.php';
chrome.runtime.sendMessage({location: location}, function(response) {});
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request.location);
});
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.
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.
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
I'd like to have my background page watch the URL and call chrome.tabs.executeScript on certain URLs. What API should I call to watch the URL in such a manner?
chrome.tabs.onUpdated.addListener can be used to detect tab loads. This will not detect navigation within frames though:
chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
if (info.status === 'complete' && /some_reg_ex_pattern/.test(tab.url)) {
// ...
}
});
For this purpose, you'd better use a content script, with all_frames set to true. Within the content script, you can inject code using the methods as described in this answer. Then use the page's location object to filter URLs.