Loading an image from a chrome extension on a page - google-chrome-extension

I've got some images in my chrome extension that I want the user to be able to inject into their page when they are using the extension.
The images will show up in the extension pop-up window, but when the user clicks the button to inject them into the page, the page can't access them/see them for some reason. I know there are specific ways of injecting JS and CSS into the page (already doing that) but I don't see any way to do the same thing with images.
I've got the following permissions set in my manifest (added the chrome-extensions:// one hoping that would do it):
"permissions" : [ "tabs", "http://*/*", "https://*/*", "chrome-extension://*/*" ]
Specifically, I'm trying to change the favicon, kind of like this (I've also tried without the leading /, and with chrome.extension.getURL("favicons/example.png")):
iconURL = "/favicons/example.png";
var link = document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = iconURL;
this.removeLinkIfExists();
this.docHead.appendChild(link);
This code works perfectly if the iconURL is a fully qualified http:// address...
You can see the actual code at my github repo here (favicon.js line 54, called by tabdisplay.js line 260).

In case people are having this problem on Chrome 17 or later, it's because the manifest must include the web_accessible_resources section to allow an image packed within the extension to be injected into a web page.
web accessible resources

Instead of:
iconURL = "/favicons/example.png";
It should be:
iconURL = chrome.extension.getURL("/favicons/example.png");
which returns absolute URL to a file inside extension folder.
Also remove chrome-extension://*/* from manifest as it doesn't do anything.

You can use from CSS as well, make sure to add this image into manifest as well.
"web_accessible_resources": [
inside CSS:
background-image: url("chrome-extension://__MSG_##extension_id__/image.png");

Since you are trying to add an image from the extension into a web page, there are security measures that do not allow directly using a chrome:// url to load things like images.
A solution I can think of is to encode the image into a data uri and sending it as text then using that as the src of the img.
iconURL = "/favicons/example.png";
var link = document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
//convert iconURL into data uri (data:image/png;base64,...)
link.href = iconURL;
this.removeLinkIfExists();
this.docHead.appendChild(link);
However, the only way I can think of to do that conversion is using a canvas element.

The STEEL answer is Ok but limited, if you want to introduce an image in the document, not as background of a div, you can do as follow.
<img id="divId" src="chrome-extension://__MSG_##some_Chrome_Extension_Id/path/image.png">
in the manifest file you does need include.
"web_accessible_resources": ["image.png","anotherImage.png"]

Related

Chrome Extension's browser action with a popup that inject/delete CSS

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.

How to download a file via a Chrome Content Script?

This SO answer details how to download a file via a Chrome Extension, but I am using a Content Script, which has limited access to Chrome's APIs. In other words, I don't have access to the chrome.downloads object. I also tried this vanilla JS solution, but it did not work for me. Does anyone have a solution for Content Scripts or know why the second solution doesn't work?
Write a background page or event page and do it from there using the example in your linked answer. Communicate from/to the content script with chrome messages.
If you mean causing a file to be downloaded to the user's computer, this code will work in a Chrome extension content script or in the JS script an a regular webpage:
Firstly, you can cause a file to download without any JS at all by simply adding the "download" attribute to an anchor tag. With this tag, instead of navigating to the URL in the "href" attribute, the URL will be downloaded.
<a href="http://website.com/example_1.txt" download="saved_as_filename.txt" id="downloader">
static download link</a>
To update the URL dynamically:
var theURL = 'http://foo.com/example_2.txt';
$('#downloader').click(function() {
$(this).attr('href',theURL);
});
If you want the download to be initiated by something other than clicking on a link, you can simulate a click on the link. Note that .trigger() won't work for this purpose. Instead you can use document.createEvent:
$('#downloader').css('display','none');
function initiateDownload(someURL) {
theURL = someURL;
var event = document.createEvent("MouseEvent");
event.initMouseEvent("click", true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
// dispatch event won't work on a jQuery object, so use getElementById
var el = document.getElementById('downloader');
el.dispatchEvent(event);
}
initiateDownload('example_3.txt');

I am trying to retrieve a webpage html with webclient and display it with webbrowser in C#

I have the following code snippet:
WebClient client = new WebClient();
String htmlCode = client.DownloadString(newurl);
webBrowser1.DocumentText = htmlCode;
BTW, webBrowser1 is defined globally elsewhere in the program. Likewise, "newurl" is a valid url also defined globally elsewhere.
WebClient gets the complete html which I pass to webbrowser1 using DocumentText.
This result is all kinds of link, syntax, remote javascript, and other errors as though the html is corrupted. However, if I use
webbrowser1.Navigate(newurl);
the target page displays just fine.
I am getting the source html so I can make changes before I display it.
Clearly I am missing something.
Any thoughts?
Regards,
Jim
webBrowser1.DocumentText = htmlCode; will set the HTML only, but will not load any linked-in resources, such as JS, images, CSS, ... .
If you want to do, what you seem to want to do, you can e.g. load the HTML via a WebClient, rewrite it (this includes changing relative paths to absolute ones or setting a base url), write it to a file, then webbrowser1.Navigate("file://path/to/file");

Is this a Google Chrome extensions bug?

I wanted to make a Google Chrome extension that needed to list the <script> tags on a web page. So I made a manifest.json with a script that would "run_at": "document_start" only on that particular web page. When I loaded the page, I noticed that the value of document.getElementsByTagName('script') was that of an array with the proper <script> element in it, but its length was 0 and I couldn't access the elements.
var scripts = document.getElementsByTagName('script');
console.log(scripts.length); // 0
console.log(scripts); // [<script type=​"text/​javascript">​...</script>​] with expected JavaScript in it
Something somewhere is definitely wrong. The scripts variable has length 0 but the console shows it contains an element which cannot be accessed.
From the documentation:
In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.
If you need access to the DOM, change run_at to document_end or leave it as the default document_idle.

Preventing iframe caching in browser

How do you prevent Firefox and Safari from caching iframe content?
I have a simple webpage with an iframe to a page on a different site. Both the outer page and the inner page have HTTP response headers to prevent caching. When I click the "back" button in the browser, the outer page works properly, but no matter what, the browser always retrieves a cache of the iframed page. IE works just fine, but Firefox and Safari are giving me trouble.
My webpage looks something like this:
<html>
<head><!-- stuff --></head>
<body>
<!-- stuff -->
<iframe src="webpage2.html?var=xxx" />
<!-- stuff -->
</body>
</html>
The var variable always changes. Although the URL of the iframe has changed (and thus, the browser should be making a new request to that page), the browser just fetches the cached content.
I've examined the HTTP requests and responses going back and forth, and I noticed that even if the outer page contains <iframe src="webpage2.html?var=222" />, the browser will still fetch webpage2.html?var=111.
Here's what I've tried so far:
Changing iframe URL with random var value
Adding Expires, Cache-Control, and Pragma headers to outer webpage
Adding Expires, Cache-Control, and Pragma headers to inner webpage
I'm unable to do any JavaScript tricks because I'm blocked by the same-origin policy.
I'm running out of ideas. Does anyone know how to stop the browser from caching the iframed content?
Update
I installed Fiddler2 as Daniel suggested to perform another test, and unfortunately, I am still getting the same results.
This is the test I performed:
Outer page generates random number using Math.random() in JSP.
Outer page displays a random number on the webpage.
Outer page calls iframe, passing in a random number.
Inner page displays a random number.
With this test, I'm able to see exactly which pages are updating, and which pages are cached.
Visual Test
For a quick test, I load the page, navigate to another page, and then press "back." Here are the results:
Original Page:
Outer Page: 0.21300034290246206
Inner Page: 0.21300034290246206
Leaving page, then hitting back:
Outer page: 0.4470929019483644
Inner page: 0.21300034290246206
This shows that the inner page is being cached, even though the outer page is calling it with a different GET parameter in the URL. For some reason, the browser is ignoring the fact that the iframe is requesting a new URL; it simply loads the old one.
Fiddler Test
Sure enough, Fiddler confirms the same thing.
(I load the page.)
Outer page is called. HTML:
0.21300034290246206
<iframe src="http://ipv4.fiddler:1416/page1.aspx?var=0.21300034290246206" />
http://ipv4.fiddler:1416/page1.aspx?var=0.21300034290246206 is called.
(I navigate away from the page and then hit back.)
Outer page is called. HTML:
0.4470929019483644
<iframe src="http://ipv4.fiddler:1416/page1.aspx?var=0.4470929019483644" />
http://ipv4.fiddler:1416/page1.aspx?var=0.21300034290246206 is called.
Well, from this test, it looks as though the web browser isn't caching the page, but it's caching the URL of the iframe and then making a new request on that cached URL. However, I'm still stumped as to how to solve this issue.
Does anyone have any ideas on how to stop the web browser from caching iframe URLs?
This is a bug in Firefox:
https://bugzilla.mozilla.org/show_bug.cgi?id=356558
Try this workaround:
<iframe src="webpage2.html?var=xxx" id="theframe"></iframe>
<script>
var _theframe = document.getElementById("theframe");
_theframe.contentWindow.location.href = _theframe.src;
</script>
I have been able to work around this bug by setting a unique name attribute on the iframe - for whatever reason, this seems to bust the cache. You can use whatever dynamic data you have as the name attribute - or simply the current ms or ns time in whatever templating language you're using. This is a nicer solution than those above because it does not directly require JS.
In my particular case, the iframe is being built via JS (but you could do the same via PHP, Ruby, whatever), so I simply use Date.now():
return '<iframe src="' + src + '" name="' + Date.now() + '" />';
This fixes the bug in my testing; probably because the window.name in the inner window changes.
As you said, the issue here is not iframe content caching, but iframe url caching.
As of September 2018, it seems the issue still occurs in Chrome but not in Firefox.
I've tried many things (adding a changing GET parameter, clearing the iframe url in onbeforeunload, detecting a "reload from cache" using a cookie, setting up various response headers) and here are the only two solutions that worked from me:
1- Easy way: create your iframe dynamically from javascript
For example:
const iframe = document.createElement('iframe')
iframe.id = ...
...
iframe.src = myIFrameUrl
document.body.appendChild(iframe)
2- Convoluted way
Server-side, as explained here, disable content caching for the content you serve for the iframe OR for the parent page (either will do).
AND
Set the iframe url from javascript with an additional changing search param, like this:
const url = myIFrameUrl + '?timestamp=' + new Date().getTime()
document.getElementById('my-iframe-id').src = url
(simplified version, beware of other search params)
After trying everything else (except using a proxy for the iframe content), I found a way to prevent iframe content caching, from the same domain:
Use .htaccess and a rewrite rule and change the iframe src attribute.
RewriteRule test/([0-9]+)/([a-zA-Z0-9]+).html$ /test/index.php?idEntity=$1&token=$2 [QSA]
The way I use this is that the iframe's URL end up looking this way: example.com/test/54/e3116491e90e05700880bf8b269a8cc7.html
Where [token] is a randomly generated value. This URL prevents iframe caching since the token is never the same, and the iframe thinks it's a totally different webpage since a single refresh loads a totally different URL :
example.com/test/54/e3116491e90e05700880bf8b269a8cc7.html
example.com/test/54/d2cc21be7cdcb5a1f989272706de1913.html
both lead to the same page.
You can access your hidden url parameters with $_SERVER["QUERY_STRING"]
To get the iframe to always load fresh content, add the current Unix timestamp to the end of the GET parameters. The browser then sees it as a 'different' request and will seek new content.
In Javascript, it might look like:
frames['my_iframe'].location.href='load_iframe_content.php?group_ID=' + group_ID + '&timestamp=' + timestamp;
I found this problem in the latest Chrome as well as the latest Safari on the Mac OS X as of Mar 17, 2016. None of the fixes above worked for me, including assigning src to empty and then back to some site, or adding in some randomly-named "name" parameter, or adding in a random number on the end of the URL after the hash, or assigning the content window href to the src after assigning the src.
In my case, it was because I was using Javascript to update the IFRAME, and only switching the hash in the URL.
The workaround in my case was that I created an interim URL that had a 0 second meta redirect to that other page. It happens so fast that I hardly notice the screen flash. Plus, I made the background color of the interim page the same as the other page, and so you notice it even less.
It is a bug in Firefox 3.5.
Have a look..
https://bugzilla.mozilla.org/show_bug.cgi?id=279048
I set iframe src attribute later in my app. To get rid of the cached content inside iframe at the start of the application I simply do:
myIframe.src = "";
... somewhere in the beginning of js code (for instance in jquery $() handler)
Thanks to
http://www.freshsupercool.com/2008/07/10/firefox-caching-iframe-data/
I also had this problem in 2016 with iOS Safari. What seemed to work for me was
giving a GET-parameter to the iframe src and a value for it like this
<iframe width="60%" src="../other/url?cachebust=1" allowfullscreen></iframe>
I also met this issue, after trying different browsers, and a ton of trial and error, I came up with this solution, which works well in my case:
import { defineComponent } from 'vue'
import { v4 as uuid } from 'uuid'
export default defineComponent({
setup() {
return () => (
// append a uuid after `?` to prevent browsers from caching it
<iframe src={`https://www.example.com?${uuid()}`} frameborder='0' />
)
},
})
If you want to get really crazy you could implement the page name as a dynamic url that always resolves to the same page, rather than the querystring option?
Assuming you're in an office, check whether there's any caching going on at a network level. Believe me, it's a possibility. Your IT folks will be able to tell you if there's any network infrastructure around HTTP caching, although since this only happens for the iframe it's unlikely.
Have you installed Fiddler2?
It will let you see exactly what is being requested, what is being sent back, etc. It doesn't sound plausible that the browser would really hit its cache for different URLs.
Make the URL of the iframe point to a page on your site which acts as a proxy to retrieve and return the actual contents of the iframe. Now you are no longer bound by the same-origin policy (EDIT: does not prevent the iframe caching issue).

Resources