Prevent access to parent from iFrame - javascript

I have a website, which has a few ad banners on it, and the banners. Many of these banners try to access the parent window. I need to add the website into an iFrame for preview purposes, so editors can see both the desktop and the mobile website. This works quite well, but some banners try to access the parent window, and when the parent window no longer is the main website, the content is overwritten, so the iFrame only displays the ad.
I have tried different ways to set disable access to parent window from iFrame. In HTML 5 there is an sandbox attribute for the iFrames, but I need to enable so many things, such as allow-scripts, that the banners still get access to the parent frame.
I have tried several different variants of the following code, on the page that is rendered inside the iframe:
// Variant 1
window._parent = window.parent;
window.parent = null;
// Variant 2
window.parent = "";
// Variant 3
window.parent = window;
// Variant 4 - from the parent document
window.onload = function() {
var frm = document.getElementById('childFrame');
var win = frm.contentWindow || (frm.contentDocument && frm.contentDocument.parentWindow) || (frm.document && frm.document.parentWindow);
if (win) win.parent = null;
}
And a lot more, but you can probably see the concept, however nothing seem to work properly. Often it seems like everything goes in to a infinite loop, which makes the browser unresponsive.

Related

Cross Domain IFrame element.scrollIntoView() Safari Issue

I have ran into an issue that is plagued all over google but none of the provided solutions work correctly. I assume the majority of these solutions do not account for cross domain.
I have a website which I would like to instruct users to embed into their site with a (full page) IFrame. The problem is on some versions of Safari only. Elements within the IFrame can not scroll themselves into view.
I notice that if I do a same domain test the IFrame can scroll itself using window.parent.scrollTo(0,element.top). This works, but not cross domain. Another odd thing is that no other browser requires the window.parent method to scroll the IFrame, only Safari. All other browsers can use element.scrollIntoView() from within the IFrame. Note that I already use the JavaScript workaround to please Safari with cross-protocol IFrames.
Another issue I've only seen on Safari Mobile inside IFrame is that Bootstrap Modals appear out of view at the top of the IFrame when scrolled down. Although, I'm sure if we can correctly set the scroll position we should be able to set the modal position as well.
Here's what I've tried;
1. window.frames['IFrameName'].document.
getElementById("elmId").scrollIntoView();
Offset trick
Velocity.js
My last resort here (I think) is to use postMessage from within my IFrame to notify the parent domain to set the scroll position of the frame.
It seems to me that this issue has been around for an awful long time. Is there a better approach than this?
This ended up being a lot more research than code. What was going on was - I had code that resized the IFrame based on the content.
In all other browsers this works fine and eliminates the scroll bars. Turns out that Safari automatically sizes the Iframe leaving scrolls of it's own. In my application there are zero static pages. This leaves me with the issue of not being able to use the scrolling=no fix described in the link.
After discovering exactly what was going on I took a different approach to fixing elm.scrollIntoView(). The code is more comments then anything but the important parts are;
Detecting when to apply the Iframe fix with RequiresIframeScrollFix
Using elm.getBoundingClientRect().top to get our scroll position from within the Iframe.
Communicating to the parent to scroll with window.parent.postMessage
Receiving the message in the parent with window.addEventListener('message',...)
Here's what it looks like.
Iframe Site
Our Iframe site currently scrolls it's elements into view like this elm.scrollIntoView(); We've changed that to the following.
if (RequiresIframeScrollFix())
window.parent.postMessage(elm.getBoundingClientRect().top, "*"); // Tell IFrame parent to do the scrolling. If this is not a test environment, replace "*" with the parent domain.
else
elm.scrollIntoView(); // If not scroll into view as usual.
Optional: fix for bootstrap modal positioning in IOS IFrames using elm.getBoundingClientRect().top.
$('#modalId').css('top', elm.getBoundingClientRect().top); // This fixes modal not in view on Safari Iframes.
RequiresIframeScrollFix() is mostly made up of some well document code laying around SO to determine if we're in an Iframe on the IPad or IPhone.
// Used to help identify problematic userAgents.
var debugNavigator = false;
// Detects an issue on mobile where the Parent is an iframe which cannot have it's scroll bars removed.
// Presumably not a bug as safari will autosize it's iframes: https://salomvary.com/iframe-resize-ios-safari.html
// Can use "scrolling=no" fix instead if the parent knows the initial size of your iframe.
function RequiresIframeScrollFix() {
try {
// Debug navigator Agent
if (debugNavigator)
alert(navigator.userAgent);
// We know this issue happens inside an IFrame on;
// Safari iPhone
// Safari iPad
// Safari Desktop Works fine.
// Check for safari
var is_safari = navigator.userAgent.indexOf("Safari") > -1;
// Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
if ((is_chrome) && (is_safari)) { is_safari = false; }
// If we need to narrow this down even further we can use a more robust browser detection (https://stackoverflow.com/questions/5916900)
// Problematic browsers can be adjusted here.
if (is_safari && inIframe() && (
navigator.userAgent.match(/iPad/i) ||
navigator.userAgent.match(/iPhone/i)
))
return true;
else
return false;
} catch (e) {
alert(e.message);
}
}
// (https://stackoverflow.com/questions/326069/)
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
Parent Site
Our parent site holds the IFrame that was automatically sized by Safari Mobile. Therefore the parent site now has scroll bars of it's own, and not the IFrame. We set our listener up inside the parent site to scroll itself when it receives a message from the IFramed site.
// Safari Mobile Iframe Cross Domain Scroll Fix.
window.onload = function () {
// Calback function to process our postMessages.
function receiveMessage(e) {
try {
// Set the scroll position from our postMessage data.
// Non-Test pages should uncomment the line below.
//if (e.origin.includes("your-iframe-domain.com"))
window.scrollTo(0, e.data);
}
catch (err) {
}
}
// Setup and event to receives messages form our iframe (or window)
window.addEventListener('message', receiveMessage);
}
Hopefully this helps someone else dissect Safari Iframe problems on mobile. Also, let me know if I've overlooked a better solution.
The other option is iframeResizer library. There are two methods you can use from within iframePage: scrollTo and scrollToOffset, which do pretty much the same what you've described - they communicate via messages. It solved this problem for us in Safari.
Inside the parent page, when setting up resizing for the iframe, you have to assign a callback function to its onScroll event:
iframeNode.iframeResize({
...,
onScroll: ({x,y}) => callback
}
And inside iframe page:
if('parentIFrame' in window){
window.parentIFrame.scrollTo(0, someNode.offsetTop);
}

Get hosting XULDocument for a ContentWindow or HTMLDocument in Firefox extension?

I have an Firefox Extension with a XUL overlay containing some Javascript with some event handlers. Among those is an EventListener for DOMContentLoaded. I have a second overlay I only want to load when visiting a certain website.
Here's some of the code I have so far:
var appcontent = document.getElementById("appcontent"); // browser
if (appcontent) appcontent.addEventListener("DOMContentLoaded", onPageLoad, true);
function onPageLoad(event) {
var doc = iEvent.originalTarget; // doc is document that triggered "onload" event
var win = doc.defaultView;
if (doc.nodeName != 'document') return; // only documents
if (win != win.top) return; //only top window.
if (win.frameElement) return; // skip iframes/frames
if(doc.location.href.search('http://somewebsite.com/') > -1) {
//Find XULDocument somehow
//var xulDoc = ??????;
xulDoc.loadOverlay('chrome://myextension/content/secondoverlay.xul', null);
}
}
How can I retrieve the XULDocument hosting the DOM, given the DOMContentLoaded event data?
Well, the XUL window is just window, aka. the global scope.
So the following two lines should both work and should be the same:
window.document.loadOverlay(...);
document.loadOverlay(...);
However, this most is not really what you want, because the XUL window is still the main browser.xul which hosts all content windows. There is no dedicated XUL window per content window!
Loading the second overlay will overlay the whole browser window, not just the "tab" (or whatever) and will stay once you close the tab (content window) or navigate away.
Now, the question is: What do you really want to achieve?
Display some UI (toolbar button, menu, whatever) only for certain pages? Then usually you'd overlay all your stuff once (on the initial load, i.e. in the "first" overlay) and just hide/show your UI according to your rules. See Tabbed Browser for some code snippets when dealing with tab switching and/or page loads.
Or you really want to apply something to the content window itself. Then you'd usually just modify the DOM of the content window directly.

iframe contents disappear when manipulated by mootools

I've got a third party (mootools) library creating tabs and I've got google double click for publishers (dfp) creating ads. dfp creates the ads in an iframe then the tabs script grabs an anchestor of the iframe and 'messes' with it to create the tabs. The contents of the iframe gets lost in the process.
I'm looking for a way of coping with this (tried firing the dfp stuff after the tabs had loaded but then the google scripts crashed).
The iframe is from a different domain to the parent window so anything which tries to do stuff to elements within the iframe is going to fail.
addTab: function(text, title, content) {
var grab = $(content);
var container = (grab || new Element('div'))
.setStyle('display', 'none')
.addClass(this.options.classContainer);
this.wrapper.adopt(container);
var pos = this.tabs.length;
var evt = (this.options.hover) ? 'mouseenter' : 'click';
var tab = {
container: container,
toggle: new Element('li').grab(new Element('a', {
href: '#',
title: title
}).grab(
new Element('span', {html: text})
)).addEvent(evt, this.onClick.bindWithEvent(this, [pos])).inject(this.menu)
};
if (!grab && $type(content) == 'string') tab.url = content;
this.tabs.push(tab);
return this.fireEvent('onAdded', [tab.toggle, tab.container, pos]);
},
Whenever an iframe or an ancestor of it are disconnected from the DOM, the iframe's contents will be cleared and the iframe will reload when it or its ancestor is reinserted back. This anoying behaviour occurs in Firefox and Webkit, but I think the iframes won't reload on IE.
As a workaround, you will need to find a way to reorder your code so that the iframe is only added after the tab script is done doing its thing to the container.
Chrome also apparently has an option that causes the iframe to not reload if you move it with adoptNode instead of apendChild but I do not believe it is cross-browser.
for more, see this similar question: How to move an iFrame in the DOM without losing its state?

Accessing parent DOM/function from within an Iframe embedded in Windows 7 gadget

I have the following issue, Im creating a windows 7 gadget, which uses Iframe to load content.I have full control on the contents of the Iframe, what I want to do is, call a function in the parent (windows 7 gadget html document), from within this Iframe, or even trigger a flyout from within the Iframe, when there is hover on a link or something.
Any help is greatly appreciated.
Thanks
Although Windows Desktop Gadgets were initially said to be excluded from the restrictions of the Same Origin Policy, that is only true of XMLHttpRequests. If the <iframe> is pointing to a page on the www, then any communication between the framed page and the hosting gadget will be blocked. If this is the case then you might be able to use the method of cross-domain communication that relies on changing the hash of the topmost window. From inside the frame, you would do something like this:
window.top.location.hash = "#ShowFlyout";
Then, in the code for the gadget you'd have something like this:
window.setInterval(function () {
if (window.location.hash == "#ShowFlyout") {
window.location.hash = "";
System.Gadget.Flyout.file = "flyout.htm";
System.Gadget.Flyout.show = true;
}
}, 100);
I don't have my windows machine on hand to test it right now, but you could try it nonetheless.
If the iframe is pointing to a html document on the local machine, then you should be able to access the global System variable as a member of the topmost window object — which is the gadget — like this:
var System = window.top.System;
System.Gadget.Flyout.file = "some.htm";
System.Gadget.Flyout.show = true;
Or, also assuming you have control over the content of the flyout, you could set an event handler on all links with jQuery (since you tagged it):
$("a", iframe.contentWindow.document).click(function () {
System.Gadget.Flyout.file = this.href;
System.Gadget.Flyout.show = true;
});

Find window previously opened by window.open

We've got the following situation, running from a single domain:
Page A uses window.open() to open a named window (a popup player). window.open() gives page A a reference to the window.
User now reloads page A. The reference to the named window is lost. Using window.open() to "find" the window has the unfortunate side effect of reloading it (undesirable). Is there any other way to get a reference to this window?
Try this:
var playerUrl = 'http://my.player...';
var popupPlayer= window.open('', 'popupPlayer', 'width=150,height=100') ;
if(popupPlayer.location.href == 'about:blank' ){
popupPlayer.location = playerUrl ;
}
popupPlayer.focus();
It will open a blank window with a unique name. Since the url is blank, the content of the window will not be reloaded.
AFAIK, no there isn't..
A kind-of-dirty-but-i-guess-it-will-work hack would be to periodically reset the reference on the parent window from within the popup using window.opener, with something like this code:
setInterval(function() {
if(window.opener) {
window.opener.document.myPopupWindow = window
}
}, 100)
In the parent window, you'll be able to access document.myPopupWindow, even after a reload (well, 100ms after the reload). This should work cross browser.
Actually what you did is destroy the parent (page A) of the created window (Popup), so it has no more reference to the original parent therefore you can't get a direct reference.
The only solution I can think of is using a browser that offers you added javascript capability to cycle through active windows (tabs) and find one that has a special property (ie: your reloaded page A) that gets recognized by the popup.
Unfortunately I guess only firefox has some added capability or extension that gives you this flexibility. (it is also a security risk though)
This should work. Add this code in the popup:
function updateOpener() {
if (window.opener)
window.opener.document.myPopupWindow = window;
else
setTimeout(updateOpener, 100);
}
updateOpener();
And this in onload of the parent window. To make sure myPopupWindow have been set wait 100 ms before accessing it.
setTimeout(function() {
if (document.myPopupWindow)
document.myPopupWindow.focus();
}, 100);
If all the windows share a common Url origin you can register a ServiceWorker and then access all windows from the ServiceWorker: https://developer.mozilla.org/en-US/docs/Web/API/Clients
AFAIK You won't be able to pass a reference to other windows from WorkerService to your window but you can establish communications with the ServiceWorker via
https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage
https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage
It Might help someone, If you opened an child tab and after refreshing the parent tab, you still want to focus on that child tab instead of opening new child tab: -
const chatPopup = window.open('', 'chatPopup');
if (chatPopup.location.href === 'about:blank' || !chatPopup.location.href.includes('/chat')) {
this.openNewWindow = window.open('/chat', 'chatPopup');}

Categories