Remove iframe from DOM without causing an error - javascript

Removing an iframe with:
const parentEl = document.getElementById('some-div')
const iframeEl = parentEl.querySelector('iframe')
parentEl.removeChild(iframeEl)
is causing an intermittent error "Failed to fetch" on line parentEl.removeChild(iframeEl). It pauses the debugger every time and I'm looking for a workaround to prevent dev tools from showing the error and stopping here.
So far I tried:
Call stop() inside the iframe to prevent it making any request
It's not possible to access iframe.contentWindow.document becuase it's a cross-origin iframe.
Add iframe.addEventListener('load', () => {}) to see if the iframe loaded
It only works for an initial load, but the frame seems to load some other stuff. There's no visibility of what it's loading
Add try catch around parentEl.removeChild(iframeEl)
Didn't work. The debugger keeps stopping at this line with the same "Failed to fetch" error
Solution, but only for frame content that can be changed:
This will not work with any 3rd party content only when having control over the frame source. Any fetch inside the frame which gets interrupted can have its error suppressed with a catch.

Related

Javascript, track iframes redirecting top window

Since there is no way to prevent an iframe from redirecting the top frame besides sandboxing which prevents other features required for viewability tracking I would like to track redirects. Since one site can have more than one iframe, it could be any of these.
Is there any way to track/find out which one (specific iframe) caused the top frame redirect?
Here is a sandbox (use browser console and enable preserve log):
Note the iframe content is usually cross domain. For ease of use its within the sandox.
We can access to the iframe content with somethig like iframe.contentWindow.document but this is possible if we observe Same-origin policy.
Another approach could be setting a Content-Security-Policy header like:
<meta http-equiv="Content-Security-Policy" content="frame-src http://example.com">
This header in the parent page prevents to load sites different to http://example.com in frames, There is also a way to report the refuse behavior sending a post but unfortunately can't be setting with <meta> tag (it's only server side). With this approach we have to perform a white list, so I think maybe it's not useful in this case. But, if the white list is given the first time, is possible to set all sites available, so when the iframe redirect, browser will refuse to load it.
If it's not the case of same-origin and the possibility of performing a white list, then I think the better we can do is calling iframe onunload event, unfortunately this event are going to be fired also when iframe page reloads not only on redirection. I think it's the closest approach. To achieve that, this code works.
var srcs = ["iframe2.html","iframe.html","iframe2.html"];
for (let i = 0; i < srcs.length; i++) {
var iframe = document.createElement('iframe');
iframe.src = srcs[i];
iframe.name = "i"+i;
document.body.appendChild(iframe);
window["i"+i].onunload = function(){console.log("change "+i)}
}
Of course onunload is fired the first time, when all iframes load, so redirections are 2th 3th and so on. But we could exclude that first case.
Here a full example https://codesandbox.io/s/o16yk7mqy , I've created iframe3.html that doesn't refresh neither reload to show clearly the point. Also I've created a simple List of redirect or reload iframes.
UPDATE
As I understand now, what you want is to set iframes with sandbox property and whitelist all what you want but without allow-top-navigation, something like:
<iframe src="iframe.html" sandbox="allow-script allow-forms allow-popups allow-pointer-lock allow-same-origin"></iframe>
This Example doesn't allow allow-top-navigation https://codesandbox.io/s/lpmv6wr6y9
This Example here https://codesandbox.io/s/4x8v1mojq7 allow allow-top-navigation but codesandbox prevents the frame to redirect so if we try https://4x8v1mojq7.codesandbox.io/ that is the url created by codesandbox, we could see the top frame reload.
As I said in comments, at least Chrome 64.0.3282.167, when we delegate all but allow-top-navigation when the iframe attempt to redirect top frame, it throw an exception. The behavior is different in Firefox (at least 58.0.2). Firefox deny top navigation but continues with the code.
So, as conclusion the best approach in my opinion is or a combination of sanbox and onunload or just onunload. Of course, if it could be possible, Content-Security-Policy is the safest and more flexible way. It depends of the implementation. It's almost impossible I think not to involve server side code to perform a perfect solution. There are white list to check, like this API https://developers.google.com/safe-browsing/v4/ and there are black list to check, look at this post https://security.stackexchange.com/questions/32058/looking-for-url-blacklists-of-malicious-websites .
If you have control over all your frames you can implement interaction between frames with postMessage. Flow:
Frame want to execute redirect - it sends a message to parent frame with redirect request.
Parent frame executing redirect and know which frame caused a redirect.
Parent:
window.addEventListener("message", (event) => {
// show message source (your frame)
console.log(event.source);
const message = event.data;
console.log(`Frame ID: ${message.frameId}`);
if(message.messageType === "redirect") {
window.location.href = message.redirectUrl;
}
});
Child frame:
function redirect(url) {
var message = {
messageType: "redirect",
frameId: "frame1"
redirectUrl: url
}
window.parent.postMessage(message, "*");
}
You can show a dialog box before redirecting to another domain/application and then the user can decide - to stay or leave the current application. You can also track the current target (i.e. iframe in your case).
window.onbeforeunload = function (e) {
console.log(e.currentTarget.location.href);
return 'Stop redirection. Show dialog box.';
};
From the individual iframes, can you set a cookie when the redirect happens? Say it happened from iframe1, you may set a cookie like
document.trackFrame = "FrameName=iframe1";
And once the redirect completes, can you try reading the cookie and there by determine which iframe caused the re-direct?

Can't send message from content script to devtools panel

I'm creating an extension for Firefox and Chrome using the WebExtensions API. I have a content script that runs on every page and a GUI in a DevTools panel. Every time the user clicks an element, the content script needs to pass a message to update the GUI in the panel. The code in the content script seems to be working, and looks like this:
content_script.js
document.addEventListener('click', (e) =>
console.log('content_scripts interpreted click on ', e);
browser.runtime.sendMessage({"target": e.target.toString()});
});
The panel is supposed to listen for these click events and update the GUI. That code looks like this:
panel.js
function notifyOnClick(message) {
console.log('extension heard click', message);
let clickTargetToString = message.target;
$('#menu').append(`<p>Clicked ${clickTargetToString}</p>`);
}
browser.runtime.onMessage.addListener(notifyOnClick);
However, every time I click within the document, the "interpreted click" line prints, and an error appears in the panel's console. It says "Error: Could not establish connection. Receiving end does not exist."
I don't know how this could be the case, since I've clearly subscribed to messages on the receiving end. Please help explain this error - thanks!
You may refer with this thread. The suggested action is to use a background pages instead of a background script. Also this link states your error means that there is no corresponding listener for that message. In some circumstances this may be expected but obviously most of the time it is an unexpected error. Make sure you are adding the content script and sending the messages from the same namespace.

Setting about:blank as the source of an iframe has side-effects in IE

I have a JS code that creates a hidden iframe, with src="about:blank" (so that I don't load the content of the iframe if the user won't click to open it), and when the user wants to see the content of the file, I simply change the src of the iframe.
The whole code would be too much to paste, but the gist of it is:
// executed on document ready
iframeView = document.createElement('iframe')
iframeView.src = "about:blank"
// executed when the user clicks the button to show the iframe
iframeView.src = "http://example.com/some-url
However, sometimes (the keyword being sometimes -- ie. about 4/5 of times), on IE the page inside the iframe throws an error and doesn't execute any JS on that page:
Origin about: not found in Access-Control-Allow-Origin header.
I'm figuring that this has something to do with the about:blank that I'm setting on pageload. But why? And why only on IE?
Edit: Tried setting the src="", that got rid of the error, but the JS on the iframe content page is still not running.

Notifying iFrame page from Firefox extension?

I'm writing a Firefox extension and need to notify an iFrame page of certain events. The iFrame page is contained within a sidebar created by the extension, and this iFrame page is controlled by me.
When I load the iFrame page, the extension code needs to send a notification and trigger something to happen within the iFrame page.
To accomplish this, I'm creating an event from the extension Javascript and firing the event, which the iFrame page is listening to.
Unfortunately, when invoking document.createEvent(), this error pops up (copied, with the quotes, straight out of Firebug):
Operation is not supported" code: "9
Any clues on the error, or suggestions on how to trigger something in an iFrame page from the extension Javascript?
Firebug helps debug web pages. From your description it appears that the problem happens in your extension, so set up the profile according to the documentation and look in the Error Console.
Other than that, remote debugging requires seeing more of your code :)
That error is NS_ERROR_DOM_NOT_SUPPORTED_ERR. Are you using the right document (the content one, not the XUL window)? Although, I'm not convinced that that would error out either (in fact, I think it should work).
This is an example of code that works for me:
var eventName = "my-event";
var event = document.createEvent('Events');
event.initEvent(eventName, true, true);
document.getElementById('my_event_listener').dispatchEvent(event)

Getting JavaScript exception happening in iframe surface

Here is the situation:
A page (iframe.html) has an iframe loading another page (iframe-content.html).
An JavaScript error might happen when iframe-content.html is loaded in the iframe.
I'd like that exception to be visible to the browser (e.g. shown in Firefox error console, or Firebug).
Here is what I see:
When iframe.html is initially loaded, and loads iframe-content.html with src="iframe-content.html", the JavaScript exception shows in Firebug.
However, if the page is loaded in JavaScript (document.getElementById('iframe').src = 'iframe-content.html'), the exception doesn't show.
You can reproduce this by going to:
http://avernet.googlepages.com/iframe.html with Firefox.
You'll see the exception as iframe-content.html is loaded.
Click on the button: the content of the iframe is loaded again, but this time the exception doesn't show in Firebug.
Is there a way at #3 to have the exception show, instead of it being silently ignored? (You can't use a try/catch around the JS code that sets the src, as this code returns immediately before the page is loaded in the iframe.)
It seems that your iframe page is not really loaded on the second time. Or it's loaded from the cache and the error is ignored. This is interesting, but I think I found an way around it.
function setContent() {
try {
console.log("Loading iframe content");
document.getElementById('iframe').src = 'iframe-content.html?foo=bar';
} catch (e) {
console.log("Caught", e);
}
console.log("Done loading");
}
With that the error should appear.
What I did, was to trick the browser to think that I'm loading a brand new page as the parameters after the url have changed.
'iframe-content.html?foo=bar';
You could replace my "bar" string with a changing timestamp. Sure, it would avoid the cache, but it would also force it to generate the error like you wished.

Categories