How to override JavaScript alert function inside an iframe before it finished loading?

Viewed 2048

I am using headless chrome browser with wraith(https://github.com/BBC-News/wraith). The problem is when I have a page that opens an alert wraith fails

ERROR: unexpected alert open: {Alert text : Bla bla bla.}
  (Session info: headless chrome=71.0.3578.98)
  (Driver info: chromedriver=71.0.3578.33 (269aa0e3f0db08097f0fe231c7e6be200b6939f7),platform=Mac OS X 10.13.6 x86_64)

This is what I tried, but has failed so far:

const BACKEND_USERNAME = "";
const BACKEND_PASSWORD = "";

function resizeIFrameToFitContent(iFrame) {
    iFrame.width = iFrame.contentWindow.document.body.scrollWidth;
    iFrame.height = iFrame.contentWindow.document.body.scrollHeight;
}


var original_url_asked = document.referrer;
if (typeof jQuery !== 'undefined') {//maybe 500 or 404 page
    $(document).ready(function () {
        //we ended up on login page, let's login
        $('body > div:nth-child(23) > div.backend_right_content > div > div > form > input[type="password"]:nth-child(3)').prop('value', BACKEND_PASSWORD);
        $('body > div:nth-child(23) > div.backend_right_content > div > div > form > input[type="text"]:nth-child(1)').prop('value', BACKEND_USERNAME);
        $('body > div:nth-child(23) > div.backend_right_content > div > div > form > input[type="submit"]:nth-child(5)').click();//submit form

        //we are logged in
        $('body').empty();//clear all the stuff
        //since we can't redirect, we'll open a full page iframe with the url we wanted to see initially
        $('body').append('<iframe id="ifr" width="100%" height="100%" frameborder="0" src="" ></iframe>');

        //load the original_url finally
        $("#ifr").attr("src", original_url_asked);//cannot redirect or wraith complains


        //hide the menu and other elements, for smaller screenshots
        $('#ifr').on('load', function () {
            $('#ifr').contents().find('div.backend_left, div.backend_right_top').remove();
            resizeIFrameToFitContent(document.getElementById('ifr'));
        });
    });
}

//important or wraith will not understand it is ready to take a screenshot
var callback = arguments[arguments.length - 1];
setTimeout(callback, 20000);//wait for page to load
2 Answers

If you have an access to a cross-origin iframe then you can override native alert function like follows:

<iframe id="ifr" src="about:blank"></iframe>
<script type="text/javascript">
var iframe = document.getElementById('ifr');

iframe.addEventListener('load', function(event)
{
    iframe.contentWindow.alert = function(s){console.log(s)};
});

iframe.src = 'iframe.html';
</script>

Code from iframe.html

<script type="text/javascript">
alert('iframe'); // this will always native function and you can not override it
</script>

<!-- BUT the following message you will get in console: -->
<input type="button" value="try it" onclick="alert('Your native alert was overriden!')">

Now you can click on the button in this iframe and you will get the message 'Your native alert was overriden!' in your console.

In other words: you can override native alert function, but you have not the possibility to override it before the iframe window was loaded. We have not some event for this case. Only after iframe window was loaded you can use your overriden function. If it is enough for you then you can use it.

But something more you can not do.

What is important to understand:

Always after setting from new src for your iframe the iframe will load original (native) alert function. And because of this you have to override it always after new site in your iframe was loaded.

Alternative solution

Thanks the hint from user Munim Munna in the comment we have a perfect solution: You can add sandbox="allow-forms allow-same-origin allow-scripts" as attribute to your iframe like follows:

<iframe id="ifr" src="about:blank" sandbox="allow-forms allow-same-origin allow-scripts"></iframe>

and all alert boxes will be ignored unless you add allow-modals to this list. In the console in this case you will get an error-message:

Ignored call to 'alert()'. The document is sandboxed, and the 'allow-modals' keyword is not set.

And if someone does not like it, than he can override the alert function like in the first part from this my answer.

You cannot prevent alert window using JavaScript, as by the time wraith executes the before_capture script the alert popup is already there. So as wraith tries to execute JavaScript leaving the alert popup open, webdriver raises the error you are encountering.

You can switch to phantomjs to resolve the issue, but if you want to stick to selenium headless chrome and wraith, you have to patch wraith to handle the modal popups properly.

File: {Ruby Installation Dir}\lib\ruby\gems\2.6.0\gems\wraith-4.2.3\lib\wraith\save_images.rb
Line: 129

driver.navigate.to url
# patch to handle rogue alert popups at page load
while true
  begin
    driver.switch_to.alert.dismiss
  rescue
    break
  end
end
# patch end
driver.execute_async_script(File.read(global_before_capture)) if global_before_capture

The while loop will dismiss as many alert popups it finds when the page navigates to your URL, and then run the before_capture script.

Related