Replacing images on a page with chrome extension - google-chrome

I'm trying to write a chrome extension that will replace some images on https://www.ourgroceries.com/ with my own images.
Specifically, I want to replace the top buttons "overview-label", "features-label", faq-label", "your-lists-label", "download-label".
I tried to use this code to replace "overview-label", but it didn't work:
window.onload = function () {document.getElementById("overview-label").style.backgroundImage="url('http://www.321space.com/content/space_telescope/thumb_small/opo0317d.jpg')";};
I tried using this script locally, and it worked, which means that maybe there's a script on this website that prevents changing these images, but I couldn't find it.
Any ideas how to replace these images?

What you need is an extension like Stylish or write you own one using contentscripts
"content_scripts": [
{
"matches": ["https://www.ourgroceries.com/*"],
"css": ["mystyles.css"]
}
],

Related

Chrome manifest Google URL

I have been trying to look for a better alternative, but I can't seem to find a better way to do this.
My current Chrome Extension manifest has about 300 lines of code that includes these kinds of URL's.
"http://www.google.com/webhp*", "https://www.google.com/webhp*",
"http://www.google.ad/webhp*", "https://www.google.ad/webhp*",
"http://www.google.ae/webhp*", "https://www.google.ae/webhp*",
"http://www.google.com.af/webhp*", "https://www.google.com.af/webhp*",
"http://www.google.com.ag/webhp*", "https://www.google.com.ag/webhp*",
"http://www.google.com.ai/webhp*", "https://www.google.com.ai/webhp*",
"http://www.google.am/webhp*", "https://www.google.am/webhp*",
"http://www.google.co.ao/webhp*", "https://www.google.co.ao/webhp*",
"http://www.google.com.ar/webhp*", "https://www.google.com.ar/webhp*",
"http://www.google.as/webhp*", "https://www.google.as/webhp*",
"http://www.google.at/webhp*", "https://www.google.at/webhp*",
I need to match URL's that run my script for https://www.google.com/ (exact), https://www.google.com/webph* (alternative for Google homepage) and https://www.google.com/search* (to match the search tabs that I want: images, videos, shopping, etc.)
The main problem lies with the fact that I can't use a wildcard for the domain extension (.com/.de/.org).
There has to be a better way right? My current manifest looks like a disaster.
I think you will always need to have your URL specifications somewhere, there is however an option to have it match in a broader way by moving it to JavaScript, and this gives you the option to move it away from the manifest if you desire.
In your manifest, simply declare the extension to inject the background script on all URLs:
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"]
}]
And then in your background script, define where the extension should work, example:
var match = 'www.google.';
var excludes = ['maps', 'whatever'];
chrome.tabs.onUpdated.addListener(function(id, info, tab) {
if (tab.status !== "complete"){
return;
}
if(tab.url.indexOf(match) !== -1 && excludes.indexOf(tab.url) === -1){
// inject your script
chrome.tabs.executeScript(tab.id, {"file": "myScript.js"});
}
}

Why can't you create Custom Elements in content scripts?

I attempted to create a custom element in a Chrome extension content script but customElements.define is null.
customElements.define('customElement', class extends HTMLElement {
constructor() {
super();
}
...
});
So apparently Chrome doesn't want content scripts to create custom elements. But why? Is it a security risk?
I can't seem to find anything in Chrome's extension guide that says it's not allowed.
I found the solution reading this page but the information was so cumbersome I wanted to write this answer for future readers (I am using Manifest v3)
Firstly, install the polyfill :
npm install #webcomponents/webcomponentsjs -D
Then add the polyfill in your content_scripts block in your manifest file :
"content_scripts": [{
"matches": [ "..." ],
"js": [
"./node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js",
"content.js"
]
}]
(important: you have to load it before your content script of course as the polyfill needs to load before you can use it)
Now it should works. Cheers
Note: the customElements feature is implemented in most modern browsers but for some reasons the interface is not available from a content script because the scripts are run in an isolated environment (not sharing the same window object space from the webpage the extension runs in).
As of now custom element can be used in chrome extensions UI. In Popup ui, option page ui and in the content script as well But it requires a polyfill which is this.
https://github.com/GoogleChromeLabs/ProjectVisBug - this is the one big custom element in the chrome extension.

How to load a content script on all Google (international) pages?

For a Google-Chrome extension, I would like to load a content script on all Google pages. What is the best way to do this?
I tried this, in the manifest.json, but it does not work:
"matches": ["http://www.google.*/*", "https://www.google.*/*"],
This "works" but it is a bit long to write and I do not think it is the best practice:
"matches": ["http://www.google.com/*", "https://www.google.com/*", "http://www.google.fr/*", "https://www.google.fr/*", "http://www.google.de/*", "https://www.google.de/*", etc..."],
See Match patterns and globs. Unfortunately, Google-Chrome doesn't have a nice mechanism for top-level-domains (TLD's) in its matches specification. So, http://www.google.*/* throws an error and http://www.google.tld/* (Greasemonkey syntax) is not supported.
To work around this, widen the matches parameter and filter the results with the include_globs parameter.
Like so:
"matches": ["http://*/*", "https://*/*"],
"include_globs": ["http://www.google.*/*", "https://www.google.*/*"],

Cannot call functions to content scripts by clicking on image

This is what I have here:
"manifest.json"
{..."permissions": [
"https:/mywebsite.com/"],"content_scripts": [{
"matches" : ["http://*/*", "https://*/*"],
"js": ["js/jquery-1.7.2.min.js", "contentScript1.js", "contentScript2.js"],
"all_frames" : true,
"run_at": "document_end"
} ]}
"contentScript1.js"
$(document).ready(function() {
$('#someDiv').load('https://mywebsite.com/index.html');}
"contentScript2.js"
function showMessage()
{alert ('Hello World!');}
"index.html"
<img src="https://mywebsite.com/images/myimage.png">
What I m actually doing here is injecting a clickable picture to the code of the the page that I m visiting and I expect that by clicking the picture a "Hello World" message will be appeared. Despite the fact that the content scripts and the picture are loaded succesfully, when I click on the image the function is not called and I get the following error in the console:
Uncaught ReferenceError: showMessage is not defined
I suppose that it cannot find the function as it is looking for it in the website that I have injected the code and not in the content scripts. But why is that, I mean if I call the function within the content script when it is loaded and not by clicking the image, the message appears. Can anyone get me out of here?
You did not understand my solution to avoid conflicts does not work with your current code. Instead of using $.noConflict, you're wrapping your script injection function in a $().ready method.
You have to remove jQuery from the "js" part of the manifest:
"js": ["contentScript1.js"],
And contentScript1.js
function injectJs(srcFile) {
var scr = document.createElement('script');
scr.src = srcFile;
document.getElementsByTagName('head')[0].appendChild(scr);
}
injectJs(chrome.extension.getURL('js/jquery-min.js'));
injectJs(chrome.extension.getURL('js/yourscript.js'));
Don't forget to add js/yourscript.js to web_accessible_resources, so that it can be used:
"web_accessible_resources": [
"index3.html",
"js/jquery-min.js"
"js/yourscript.js"
]
In js/yourscript.js, wrap your function logic in an anonymous function in conjunction with $.noConflict. $.noConflict(true) is used to avoid conflicts with scripts in the page. It restores the original value of $ and jQuery.
(function(jQuery, $) {
// Here, you can do anything you want.
// jQuery and $ refer to the same jQuery object from `js/jquery-min.js`
})(jQuery, jQuery.noConflict(true));
After looking at your question again, I noticed that you're loading content through ajax: $('#someDiv').load(...). When the script is injected, it runs in the scope of the page. That's why your AJAX call fails: The request is blocked because of the Same origin policy.
Now, we can use a different approach to fix your code. Instead of moving the logic from Content script to the page (by an injected script), we modify the page index.html. The click event is not pre-set, but added in the content script. For example:
"index.html":
<img src="https://mywebsite.com/images/myimage.png">
"contentscript2.js":
$('#showMessage').click(showMessage);
I think I m gonna answer my own question:
The reason that this happening is because content scripts run in an isolated world
see: http://code.google.com/chrome/extensions/content_scripts.html#execution-environment
So, you simply cannot call functions, once you injected some html code, in content_scripts to perform some work in the current page of user.
What you have to do is to inject your scripts in the page as you do with html code.
So:
(1) add the files you want to inject in web resources in your manifest file
see: http://code.google.com/chrome/extensions/manifest.html#web_accessible_resources
"web_accessible_resources": [
"Script2.js",
"index.html",
"jquery-1.7.2.min.js"]
(2) in contentScript1.js (load this as a content_script)
//inject your javascript files to the head of the page
function injectJs(srcFile) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=srcFile;
document.getElementsByTagName('head')[0].appendChild(scr);
}
injectJs(chrome.extension.getURL('jquery-1.7.2.min.js'));
injectJs(chrome.extension.getURL('Script2.js'));
//inject your html by loading query and passing your html page
$(document).ready(function() {
$('#someDiv').load(chrome.extension.getURL('./index.html'));}
That's all!

Could I make a Google Chrome extension for chrome pages (downloads, extensions etc)?

I'd like to make a very simple extensions that slightly alters how the Downloads page looks. Changing the History page might be interesting too, but that's for later.
Is there a way to do that?
I tried making a Content Script extension, with "chrome://downloads" as match in manifest.json. Chrome won't allow that and responds with an error when packaging the extension.
Is there another simple way? It has to be simple, because changes would be simple, because all chrome:// pages are built with HTML, JS and CSS.
edit
After trying with background scripts a little...
I can't get chrome.tabs.executeScript to work! I added in background.html:
chrome.browserAction.onClicked.addListener(function(tab) {
alert(this.document.body.innerHTML);
alert(chrome.tabs.executeScript(null, {
code : "document.body.style.backgroundColor = 'red';"
}));
});
And I added this in manifest.json to add a (invisible) 'browser action button':
,"browser_action": {
/* "popup": "background.html",*/
"name": "Alter page"
}
The onClicked event fires both alerts (first is background.html's body, second is undefined). But the code (a string with document.body.style.backgroundColor = 'red';) doesn't execute! And ofcourse there's no debugging for extensions like this =)
Any tips anyone? I'm trying to get a hold of the tab's window.document (not background.html's window.document!). An injected script (that's what chrome.tabs.executeScript is supposed to do) should do that.
PS
I'm stealing from make_page_red/manifest and make_page_red/background.html
The 'extension' I have so far: http://hotblocks.nl/js/downloads.rar
EDIT
I found out what I want to achieve is possible with just CSS. I don't need to inject javascript. Does that make it easier? Does that make it possible? =)
According to this documentation, chrome:// URLs are an invalid scheme so they won't be matched:
A match pattern is essentially a URL that begins with a permitted scheme (http, https, file, or ftp), and that can contain '*' characters.
I would look into using override pages instead.
As requested, here's my extension that can at least load when chrome://downloads is loaded, although as I said, I don't think you can modify the page even if you know that's the page you're viewing.
manifest.json
{
"name": "Test",
"version": "0.0.1",
"background_page": "background.html",
"permissions": [
"tabs"
]
}
background.html
<script>
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
{
if (tab.status == "complete")
{
alert(tab.url);
// should alert 'chrome://downloads' on that page. You can
// check for this url here and then do whatever you want
}
});
</script>
Update: Since Chrome 31 there is an API for extensions that allows access to Chrome's downloads: https://developer.chrome.com/extensions/downloads
There's also an API that allows access to list and manage other installed extensions: https://developer.chrome.com/extensions/management
(Previous Answer)
Unfortunately, there's not currently an API for Chrome extensions to access information about a user's downloads. It's a widely requested feature, though, and there's some discussion among Chrome developers here: http://code.google.com/p/chromium/issues/detail?id=12133
Star the issue if it's a feature that you'd like to see, and you'll receive email updates.
As this page shows, there is no API to override the downloads page... However, there is a way to make a file you have made replace the chrome://downloads/ page whenever it is loaded using javascript in your background page...
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(changeInfo.status === "loading"){
if(tab.url === "chrome://downloads/"){
chrome.tabs.update(tab.id, {url: "REPLACEMENT.html"});
}
}
});
Essentially what this does is - As soon as the page chrome://downloads begins loading (using the tabs.onUpdated API), the page is redirected to REPLACEMENT.html (Using tabs.update API)... There is no visible delay in the tab update
as this script is run before the chrome://downloads page begins loading... You can use a similar code in your file by pressing CTRL + U on the downloads page to view and copy its source code