Overlaying the page contents from Google Chrome extension - javascript

I am writing a very simple Google Chrome extension, but have faced such a multitude of issues that it's a bit overwhelming for a project of such a miserable scale.
The extension is a very simple one: upon arrival on any page (say, google.com) the page contents is hidden and the user is faced with a question (s)he has to answer correctly... or be redirected to some other page that provides the correct answer. In other words, the user cannot access pages on the Internet unless (s)he answers the questions correctly.
To hide the page contents I decided to go for a simple overlay using the following approaches:
Approach #1
I tried appending to the current document's body a simple opaque div with position: fixed;, z-index: 2147483647; and width/height at 100%. That worked, but:
The page's CSS kept interfering with the elements inside of my div.
Flash content occasionally appeared on top of it (at least on Windows XP).
Chasing embeds all over the page and setting wmode to
"transparent" didn't help, offsetting to -10000px or setting
display:none; simply alleviated but didn't solve the problem. See also this question.
Approach #2
I tried sandboxing the GUI in an iframe that is created and injected into the page to behave exactly as the div in the above approach. It perfectly solves the issues of the first approach, but:
Apparently there's no way to access the contents of the iframe because of cross-origin policy. And that access – I need it to assign handlers to the input field where the user is typing the answer, I need to remember who's stealing the focus from my answer input field to give it back once the question is answered, etc. etc. etc.
Using Message Passing didn't work for me and I'm not even sure if I should make it work because messaging makes the entire thing overly complex and prohibits me from using the application as a simple webpage (i.e. not as an extension). Why even bother?
So... where am I wrong with my approaches? Is there a third or a fourth one that I'm not aware of?
I appreciate but don't really need code as an answer. A hint or a push to the right direction would be just as good.
P.S. I suppose that at some point somebody will ask if I have code to share. I do, but there's a bunch of it. Which part specifically would you like to see?

Approach #2
Concern #1
Apparently there's no way to access the contents of the iframe because
of cross-origin policy. And that access – I need it to assign handlers
to the input field where the user is typing the answer, I need to
remember who's stealing the focus from my answer input field to give
it back once the question is answered, etc. etc. etc
Yes you access contents of iframe(s) for matter all the code of a Web Page, no CSP etc.
Content script injecting an iframe.
I suggest this is best approach , you can inject script to dynamic generated iframes as shown here and fetch content
Sample Implementation
manifest.json
{
"name": "Iframe",
"description": "",
"version": "1",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"myscript.js"
],
"run_at": "document_end"
},
{
"matches": [
"<all_urls>"
],
"js": [
"anotherscript.js"
],
"all_frames": true
}
],
"permissions": [
"<all_urls>"
]
}
myscript.js
var iframe = document.createElement("iframe");
iframe.setAttribute("src", "https://www.facebook.com/plugins/like.php?href=http://allofrgb.blogspot.in/");
iframe.setAttribute("style", "border:none; width:150px; height:30px");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
document.body.appendChild(iframe);
anotherscript.js
iframes = document.getElementsByTagName("iframe");
for (iframe in iframes){
console.log(iframes[iframe].src);
}
console.log("In another Script");
If you observe console logged messages you observe messages are logged twice (document log + iframe log + [any number of optional iframes in pages]*)
anotherscript.js which runs during document idle states does execute in dynamic generated iframe, how ever you can re-run content script through chrome.tabs.executeScript() any time.
Concern #2
Using Message Passing didn't work for me and I'm not even sure if I
should make it work because messaging makes the entire thing overly
complex and prohibits me from using the application as a simple
webpage (i.e. not as an extension). Why even bother?
What is you want to achieve?

Totally forgot about this question... In the end I went with approach 2 using message passing to communicate with the iframe and it works pretty fine. Here's a repo of the extension for those who are interested in reading the code: https://github.com/olegskl/invasive-kanji

Related

Edit Iframe with chrome extension on nike page [duplicate]

I want to ask is there ANY way or extension that can pre-highlight text within the iframe whenever a new window is opened containing iframe? I have tried many extension but none of them works.
I need to filter out content based on certain keywords and the content is within iframe. I can do it with CTRL+F but there are many keywords like 10-15 within each article to be found. So it makes my job very tough and time consuming. Few extensions that I have tried from chrome are multi highlighter, pearls, FF but none of them seems to work.
I also know the reason why these extension can't access content within the iframe i.e. due to cross origin policies.
But I also remember around an year ago I worked with chrome extension named 'Autofill' that could pre-select form elements whenever I opened new chrome window containing iframe.
So is there any work around?
You can set your extension permission to run content scripts in all frames as document at http://developer.chrome.com/extensions/content_scripts.html#registration by setting all_frames to true in the content scripts section of your manifest file. Adding to Google's example from that page, part of your manifest file might look like
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"],
"all_frames": true
}
],
...
}
You'll need to be careful since your content scripts are going to be inject into the page once for the parent page and one for each iFrame on the page. Once your content script is injected into all frames on the page you can work your magic with finding and highlighting text.
if (window === top) {
console.log('Running inside the main document', location.href);
} else {
console.log('Running inside the frame document', location.href,
[...document.querySelectorAll('*')]);
}

Rerun Firefox addon's script on URL dynamic change

To simplify the scenario, lets say I'm working on an extension that just contains: alert("Hello") whenever you load a page in example.com. Relevant manifest.json part:
"content_scripts":
[
{
"matches": ["*://*.example.com/*"],
"js": ["script.js"]
}
]
When I first visit the website, it works fine. But the problem is some of the links in the website don't reload the page, they just manipulate the DOM. So a link for example will take you to example.com/foo, the script doesn't run. Even when I return to the home page, it doesn't run again, and all the edits that were made the first time are removed.
How do I make the add-on recognize that the page has changed, and rerun the script?
After spending hours on this, I was finally able to achieve what I want, though not in the way I expected it would be. This is my solution:
document.addEventListener("click", function(){
editStuff();
});
This works just fine for the website I'm making the add-on for. There is some wasted computational power, as some clicks don't really require the function to work again, but its minimal in my use case.

Applying CSS styles from chrome extension to page 'instantly' before page fully renders

I'm new to making chrome (or even browser) extensions am trying to make one that applies some custom CSS rules to certain page elements. It seems to work generally as expected, with a few slight annoyances.
Namely, when I apply any CSS style rules using JS, there seems to be a delay between the page fully rendering in the browser to when my extension's CSS rules are applied.
One way I've found to overcome this is to add the CSS file I want to be applied instantly to the manifest file, under the content_scripts, like so:
"content_scripts": [
{
"run_at": "document_start",
"all_frames": true,
"matches": ["<all_urls>"],
"js": ["filter.js"],
"css": ["filter.css"]
}
],
But the issue now is that I want to check if the user has pressed the 'enable' button on the pop up for my extension before applying this. To do this, in the filter.js and background scripts, I check the chrome storage etc. to see if the user has the enabled flag set to true.
I then use the chrome.tabs.insertCSS method to insert my CSS file(s).
In the case where the user has pressed disable on the extension, the browser still renders the page with the effects of filter.css until it runs the JS to remove the effects. By the time this happens, the user has already seen the effects of filter.css which I don't want.
What I want is the browser to instantly apply or not apply my styles (depending on if the user has enabled/disabled) before the page is displayed to the user.
The methods of injecting the CSS thus far have all led to a delay. It must be possible to add it in or remove the CSS without a delay, as I've used extensions such as Dark Reader which seem to be able to apply their styles immediately without ever showing the browser content without their CSS.
Ideally, there would be a way to do conditional checking in the manifest, but I know this isn't possible. What else can I do?
Any help would be appreciated!
Thanks for reading!
Finally managed to fix my issue. The problem wasn't with any of the functions to insert the CSS, it was just that I was running my code to inject the CSS in the window.onload function. :facepalm:

Configuring a sidebar_action to be only on for certain sites?

I want to create a firefox extension that allows a sidebar to automatically show when a user navigates to certain web pages.
For example, let's say I configure it so if a user navigates to google.com they will be presented a sidebar that lets them see some "previous searches" they had done.
I do not want them to have to click a menu action / keyboard shortcut to display it. And I do not want to display it indefinitely.
I've been looking at these links to learn how to use sidebars:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/sidebar_action
https://github.com/mdn/webextensions-examples/tree/master/annotate-page
But they do not seem to cover how to hide/show sidebars conditionally. They are just kinda, always shown. Not really what I want.
Is there a way to use sidebars in this way or should I use another method?
You can control when your extension is inserted into the currently visited page (thus, causing your sidebar to appear) or not based on the permission settings in the manifest file
manifest.json
...
"permissions": [
...
"http://*.google.com/",
"http://*event*/",
...
],
In the above example, the extension will work only on google.com as well as any domain containing the word event. If you want to target domains where the word event appear as part of the path, then you would use something like
"http://*/*event*/*",
You get the idea.
For more information read Google's Extension Development - Declare Permissions
This can be achieved by specifying hosts which can be either one of a list of known strings (such as "geolocation") or a match pattern that gives access to one or more hosts.
Here's an example of the permissions part of a manifest file:
"permissions": [
"tabs",
"bookmarks",
"http://www.blogger.com/",
"http://*.google.com/",
"unlimitedStorage"
],

Is it possible to search the DOM for a keyword occurring within an iframe and reload page until it's found?

I have an ad that displays within an iframe on a given publisher site around every 1000 loads or so. I have no control over the host site but I need a way to see the ad live as users will see it. I'm trying to figure out a javascript solution that will load the page, search for the name of my company to see if my ad loaded (the company name is the id of a div tag that loads the iframe) and then either stop there if it finds it, or reload the page if not.
I had it sort of working by running a script in the console that got the innerHTML of the document body, searched for a keyword and then reloaded the page if the keyword wasn't found.
Two problems though.
It could only find keywords outside of the iframe.
It didn't search the content of the iframe (where the actual keyword that would identify my particular ad sits) even if I set a delay or did onload.
Secondly, for every page refresh, the script would be cleared from the console.
I know this is beginner stuff but I would love any pointers to the correct way to tackle this problem.
Thanks so much for the help thus far (also, I upvoted everyone but don't think I have the necessary cred for it to show up publicly)
Here's where I got. I created a chrome plugin with the following manifest.json:
{
"manifest_version": 2,
"name": "Ad Refresh",
"version": "1.0",
"permissions": [
"activeTab",
"tabs"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["jquery.min.js", "contentscript.js"],
"all_frames": true
}
]
}
I have the content-scripts running on all urls but will restrict it once I get things running properly.
For contentscript.js that gets injected and runs in each frame, I have:
setTimeout(function(){
$("[title='3rd party ad content']").attr("id", "dfp"); // "3rd party ad content" is the title of all iframes that could potentially contain my ad and is the only identifying attribute across all iframe instances. I stick an id on there so it's easier to grab with getElementById. It only gets the first instance though, need to figure out how to loop through all.
var company = document.getElementById('dfp');
if (company == null) {
console.log("no hit");
} else {
console.log(company);
}
}, 5000);
I'm not worried about reloading the page, I'm just stuck on getting access within the iframe.
I am unable to directly grab any element within the actual content of the iframe with jquery $ or getElementById etc. However, if I run getElementId on the iframe itself and console.log it, it includes all the HTML inside the iframe:
http://i.stack.imgur.com/dfuYt.png
I tried getting the innerHTML of the iframe element so that I'd have it as a string and could search for it, but all it returns is the iframe element tags, none of the inner content. I don't know if any of that makes sense but I appear to be over my head at this point.
Ok, last addition. My ad runs a script that I can see under "Sources" in inspector. So I thought "Why not run
var scripts = document.getElementsByTagName('script'); to get an array of all the scripts that were loaded on the page? Then I could just search the array to see if my script and hence my ad had loaded and we'd be golden." Unfortunately though, it doesn't include my script in the array, even when it's loaded and is visible in "Sources" and accurately includes a random Stripe script that's also loading from within an iframe. Bummer...
Use .load event of the jQuery to know whether iFrame is loaded and then read the innerHTML of the iframe body
Try this:
$('#ID_OF_THE_IFRAME').load(function() {
var iFrameContent = $('body', this.contentWindow.document).html();
console.log(iFrameContent);
});
Fiddle
JS:
var company = document.getElementById('myframe1').contentWindow.document.getElementById('company');
if (company == null) {
//reload
console.log("reload");
} else {
//continue
console.log(company);
}
It sounds like the iframe containing the ad is loaded from a different domain than the main page, is that right? That would explain why your JavaScript code running in the main page (or in the console, same thing) can't access DOM elements inside the iframe. Browser cross-domain security prevents that kind of access: the iframe is treated just like a separate browser window or tab.
If the main page and the iframe were both loaded from the same domain, then you could use contentWindow as a couple of answers have described. But that won't work across domains.
So, what can you do?
You're building a tool for your own use or the use of your colleagues - not something you need to publish on a website for the rest of the world to use, right?
This gives you a couple of other options. First, you could simply disable cross-domain browser security. In Chrome, you can do that as described here:
Disable same origin policy in Chrome
Beware: Don't do any "normal" browsing in a Chrome session running in this mode, only your special testing. But if you do run Chrome in this mode, then you'll be able to access iframe DOM elements via contentWindow and contentWindow.document as described in the other answers.
If that doesn't do the trick, or if you don't want to have to start a special Chrome session for this, another approach would be to write a Chrome extension. This would allow you to write code to access DOM elements in both the iframe and the main window using techniques like these:
access iframe content from a chrome's extension content script
Chrome extension to remove DOM elements inside iframes
Or you could write a Firefox extension if you prefer - similar capabilities are available in both.

Categories