I'm making a chrome extension that loads an <iframe> of another site onto the New Tab page. Right now I'm loading YouTube's subscription page (don't worry about the Same-Origin issue, I solved that already), but now I'm trying to cut everything out of the page except the #content element (the subscription feed element).
Here's my code:
Background.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="script.js"></script>
</head>
<iframe id="left" src="left.html" name="left"></iframe>
<iframe id="right" src="right.html" name="right"></iframe>
</html>
Left and Right .html
<!DOCTYPE html>
<html>
<body>
<input type="button" value="Load new document" id="loader">
</body>
<script type="text/javascript" src="window.js"></script>
</html>
Window.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("loader").addEventListener("click", loadUrl);
});
function loadUrl()
{
window.location = 'https://www.youtube.com/feed/subscriptions';
[].forEach.call(document.querySelectorAll(!'#content'),function(e){
e.parentNode.removeChild(e);
});
return false;
}
As you can see, right now I'm loading it in with a button. Once it's pressed, it loads YouTube and should cut out all the html except #content, but it's not. Is there another way to solve this, possibly with jQuery? Thanks!
document.querySelectorAll(!'#content')
won't meet your requirements, you should use the following instead if you do want to remove all elements except #content.
document.querySelectorAll('*:not(#content)')
#content isn't accessible to document, because the global document is on a different page - the page containing the iframes, but not their contents. Moreover, document.querySelectorAll(!'#content') is not a valid selector string - that will be interpreted as document.querySelectorAll(false) because ! of a non-empty string returns false.
You're going to have a hard time getting access to the contents of an iframe, just in general. Most browsers, like Chrome, won't event load the contents of an iframe if X-Frame-Options is set to SAMEORIGIN, which, for youtube, it certainly is.
Supposing you're getting around this (with a reverse proxy, perhaps?), you can get the contents of an iframe using JavaScript like so:
iframe.contentWindow.document
You can then use querySelector and friends on the iframe:
iframeDocument.querySelector('#content')
And if you want to cut out a node, you should remove() it:
iframeDocument.querySelector('#content').remove()
Now, having said all of that:
Don't do this.
You're abusing iframes, which requires some very creative (read: brittle, hacky) code, and Youtube certainly has a public API from which you can get access to someone's Youtube feed - and in a way that Youtube is far less likely to block by tightening security, or even by accident. (Suppose the HTML on their page changes, so #content contains everything. Or suppose they decide to check that the correct origin is requesting the page with JavaScript, and block you.)
What you want, you should use Youtube's API for.
Related
Is there any tricks to hide the src url in iframe? Or maybe encrypt a part of the external url?
TLDR: No, You cant.
You can prevent it appearing at browser page source using JavaScript. But people still can see it with Inspect Element option.
And if you encrypt the URL, it won't work. HTML src must have a specific URL/File path. It can't understand encrypted text.
Still, If you want to hide it from page source, Try this:
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
</head>
<body>
<iframe id="extframe" src=""></iframe>
<script src="script.js"></script>
</body>
</html>
JavaScript at script.js file:
var iframeUrl = document.querySelector('#extframe');
iframeUrl .setAttribute('src', 'https://stackoverflow.com/');
You can't. If the URL isn't in the HTML, how would the browser know where to get it?
One thing you could try is to obscure it to make it slightly harder for someone to find it. You could have the src attribute be blank and then when the document is ready fetch the URL value from the server in a separate AJAX request and update the iframe tag to include that value in the src.
This would be a fair amount of work, however, and wouldn't really accomplish anything. The only thing it would prevent is somebody finding it by viewing the page source. They can still look at the "current version" of the HTML in any web browser's debugging tools. (Right click on an element and inspect it, which is nearly ubiquitous at this point.) Or any other normal traffic-sniffing tools will see it plain as day.
Ultimately, if the web browser needs to know a piece of information, then that information needs to be visible on the client-side.
I'm working on a Chrome extension that blocks websites, but not using the Chrome built-in blocking methods. The reason for customising, is I need to display a custom block page for each website blocked - the same template, with details as to why this particular site is blocked.
In my content.js file I store a full HTML template of a 'this site is blocked' page in a var called template (as I couldn't figure out loading it from a file inside the Extension, I'm a Python coder by day...). The manifest contains a large list of URLs, so if the content.js script has been loaded, this site should be blocked (or checked to see if locally the user has allowed it with 'allow anyway'). If a site should be blocked, the only thing I've gotten to work is:
var template = `<html>...full web page template removed...</html>`;
chrome.runtime.sendMessage({method: "getSite", site: window.location.host.toString()}, (response) => {
console.log(response.data);
document.body.innerHTML = renderTemplate(template, response.data);
});
The renderTemplate function just takes the HTML and does some find + replace with the response.data - which is data about the URL, fetched from a background script that's simply a .js file with a massive dictionary.
The problem is it obviously only replaces the <body>, and the websites CSS affects the block page. Is it possible to replace the entire page contents? Trying to replace the document.body entirely throws The provided value is not of type 'HTMLElement'.
I'm still learning both JS + extension development, any advice appreciated.
Use document.write
The problem that you are running into is that you are replacing the body, but not the head (or any <script>s that reside outside of the body).
To replace all html, you want to use document.write. Depending upon how you want to overwrite, you may want to call document.open and document.close.
Example
An example replacing all html is shown below:
newHTML = `<html>
<head>
<title>A Simple HTML Document</title>
</head>
<body>
<p>This is a very simple HTML document</p>
<p>It only has two paragraphs</p>
</body>
</html>`
document.open()
document.write(newHTML)
document.close()
Verification
Using Chrome Developer Tools (should be Options > More Tools > Developer Tools), we can navigate to the console and get the content
-> document.documentElement.outerHTML
<- html
<html><head>
<title>
A Simple HTML Document
</title>
</head>
<body>
<p>This is a very simple HTML document</p>
<p>It only has two paragraphs</p>
</body></html>"
This jsfiddle example shows what this looks like, and replaces the CSS background.
I've asked this question before this one, trying to figure out why I was seeing scrollbars on the a-ads iframes, on this website, while some swear they didn't see them!
Hence, I have discovered that the appearance of the iFrame scrollbars is by execution order. And with faster or slower computers, results may vary.
I realize that I have to replace scrolling="no", a strong (but unsupported in HTML5), attribute that forces no scrollbars on iframes, with a JavaScript, or CSS alternative. However, the CSS overflow:hidden; is too weak to override whatever style the iframe's source may contain (e.g. overflow:scroll, overflow:auto, etc.). So the solution must be with JavaScript.
The caveat however, is that the javascript must activate after the iframe itself is loaded, but before the source (src="") is loaded. Because if scrolling="no" is replaced after the source is loaded it has no effect on the outcome of the iframe display. Though, if it is place before the <iframe> tag markup is reached, how does the JavaScript know what to modify? It is as good as not present at that point.
One More Problem: The source can't be stripped from the <iframe> tag, and replaced in JavaScript. We've tried that, and while it worked....sort of... we lost ad impressions and clicks into a black hole, because the spider (i.e. bot) at a-ads couldn't, or had problems with, detecting the proper a-ads code on the webpage. But if we left it in the iframe and just reloaded the source after scrolling="no" was set, then that would result in double ad-loads (i.e. invalid impressions).
This is a real pickle!
All the Einsteins of the world - You're Needed!
Also, this S.O. question doesn't apply.
Screenshots
Chrome Proof
IE Proof
Opera Proof
Firefox was best-behaved
This issue has now been solved, for now, I think. Here is the solution:
<head>
<script type="text/javascript">
function noScrollBarsOnAAdUnit( ElementID )
{
document.getElementById( ElementID ).setAttribute("scrolling", "no");
return true;
};
</script>
</head>
<body>
<script type="text/javascript">noScrollBarsOnAAdUnit( 'aa-unit-top-center' );</script>
<iframe id='aa-unit-top-center' style='width:468px;height:60px;' class='a-ads-frame' data-aa='[ad-id]' src='https://ad.a-ads.com/[ad-id]?size=468x60'>
<!-- iframe fallback message here -->
</iframe>
<script type="text/javascript">noScrollBarsOnAAdUnit( 'aa-unit-top-center' );</script>
</body>
Fellow S.O. members, please, check this page, to assure this fix is consistent, and really works at least 99.99%, if not 100%, of the time.
Thanks!
-James A.
I expect security restrictions (cross-site scripting) will prevent me from doing this, but I thought asking wouldn't hurt. I'm working on a page that may be embedded within another page from a different domain. I don't know anything about the iframe, except I could predict part of the src attribute. I can detect "if" it's embedded, but I really need to know the offset at which it's embedded.
I'm doing some position calculations within my page (that in this case is embedded in an iframe). It works great whether or not it's embedded in all current browsers but gives me problems in IE 7. For some reason the offset jQuery gives me in IE 7 is off by the exact amount of the embedded iframe offset. I could compensate for it in this case, if I could get the offset.
Thanks in advance.
You can do this with JavaScript.
Parent Frame
<html>
<body>
<iframe src="childFrame.html"></iframe>
</body>
</html>
Child Frame
<html>
<body>
Child Frame
</body>
<script type="text/javascript">
alert(document.parentWindow.document.body.innerHTML);
</script>
</html>
The key bit is here "document.parentWindow.document.body". Now you have the parent body you can access the HTML and get any properties you need.
The other way round you can use the Sandbox attribute on the Iframe. Check out the link below. This attribute is not recommended for production code.
http://www.w3schools.com/tags/tag_iframe.asp
I am using EmbeddedWB (A TWebbrowser extension) to do like a "live preview" of some dynamically generated content.
I am trying to add jQuery into the mix, so I can get some fancy effects going on, however since IE9 always asks "Allow blocked content" for each and every damn page, a dynamically generated one (Webbrowser.LoadFromString) certainly wont be allowed to have fun. To put it simple: It wont allow Javascript execution.
I tried adding a SecurityManager to my TEmbeddedWB, however that did not do it either. I tested my dynamic code in Firefox, and in IE9, and it works (of course, in IE9 I have to allow first, which was how I found it was a security issue).
Is there a painless way to get around this, without having to manually go into IE and tweak something? Or am I completely wrong about the cause of the issue?
EDIT: After trying this article's method, IE does not ask if it should allow stuff anymore, however my script is still not being executed within my TEmbeddedWB/TWebbrowser..
EDIT 2: Okay, by removing the jQuery code, and displaying a plain Alert, I am forced to conclude that JS is now being executed, however jQuery is not.
EDIT 3: Here is the (stripped down) HTML code that my app generates, where jQuery is not working in my EmbeddedWB/TWebbrowser control - however, it works in Internet Explorer 9 itself:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<script type="text/javascript" src="file://C:\jQuery.js"></script>
</head>
<body>
<center>
<p>
Some stuff here!
</p>
</center>
<script type="text/javascript" language="javascript">
$(document).ready(function(){
alert('I Am jQuery!!!!');
});
</script>
</body>
</html>
EDIT4: I have also tried switching the src to a Google Hosted jQuery, and that did not work either. Removing the Metatag did not fix it either. Just letting you know of stuff I tried before you waste time on suggesting it :)
EDIT5: By navigating to a site that uses jQuery (Webbrowser.Navigate) the site was working as expected. However when doing it from my local test.html, or by doing .LoadFromString();, it will not work.
Will not work = jQuery code not executing.
It seems to work if you use correct URL for the jquery.js file:
<script type="text/javascript" src="file://C:/jQuery.js"></script>
<script type="text/javascript" src="file:///jQuery.js"></script>
or a relative path, you can also omit the file:// protocol:
<script type="text/javascript" src="../../jQuery.js"></script>
The above works when you load the HTML from a file. The question is however, if content from memory and javascript from file system is not considered crossing a security context boundary and rejected for that reason by the embedded browser. In that case, embedding jquery directly in the HTML content (using the <script> tag) should work.