Inject chrome browser extension content script based on URL - google-chrome-extension

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.

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.

Can background page use window.location

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);
});

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 inject in frameset code

i develop some extension for google grome
i inject at document_end event my js (+jquery)
i set in manifest allFrames: true
my match url has frameset
my goal get element by id inside one of frame
i do
//wait for load my frame
$("frame[name='header']).load(function() {
//here I need get element by id inside this frame
});
how to do this properly?
PS: my frame is from the same domain
You dont need to do document load, I assume your doing this from a content script. Just place the "run_at" manifest to be document_end and within your content script you check if the current URL is that frame page, then you will be in that Domain.
Something like this:
if(location.href == 'http://someweb.com/path/to/page.html') {
var foo = document.getElementById('foo')
Something like that will get you started.

Resources