3

I want to add iframe of my Google Application Script Web to WordPress site. I want to get rid of the scroll bar. See the pic.

enter image description here

Not sure if the iframe-resizer does not work because of this error message

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided
('https://script.google.com') does not match the recipient window's origin ('http://rsness.8u.cz').

From iframe-resizer troubleshooting page looks like this error does not have to be an issue that

This error occurs when the parent window tries to send a message to the iframe before it has loaded. IFrameResize makes multiple attempts to talk to the iFrame, so if everything is working then you can safely ignore this error message.

If youre still having problems, or you really want to not ignore the error, then you can try delaying the call to iframeResize() until after the onLoad event of the iframe has fired.

not sure how I can load iframeResize() after the onLoad

If this does not fix the problem then check x-Frame-Options http header on the server that is sending the iframe content, as this can also block calls to postMessage if set incorrectly.

Google offers only two options enter image description here

  • default does not work and

  • allowall gives me an error on jsfiddle

    Invalid X-Frame-Options header was found when loading “http://fiddle.jshell.net/_display/?editor_console=true”: “ALLOWALL” is not a valid directive.

  • SAMEORIGIN Google does not accept

I read somewhere that the error Failed to execute 'postMessage' on 'DOMWindow' could be because my WordPress runs on http but Google on https. So I tried to se it up on my https site but with the same error

iframeResizer.js:800 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided
('https://script.google.com') does not match the recipient window's origin ('https://zz.zustatzdravy.cz').

it also gives a warning message

iframeResizer.js:142 [iFrameSizer][Host page: iFrameResizer0] IFrame has not responded within
5 seconds. Check iFrameResizer.contentWindow.js has been loaded in iFrame. This message can be
ignored if everything is working, or you can set the warningTimeout option to a higher value
or zero to suppress this warning.

How can I check that iFrameResizer.contentWindow.js was loaded?

I have this in body tag <script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.11/iframeResizer.contentWindow.min.js"></script>

and when I go directly to the iframe url then I can see this enter image description here

So I guess the contentWindow.js was loaded

Anyone could help me to make the hight resize?

UPDATE1 setHeight() in GAS did not help return HtmlService.createTemplateFromFile('index').evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL).setHeiht(500)

UPDATE2 minimal reproducible examples

Google files

  • .gs code

this is the whole gs server code

function doGet() {
  Logger.log("let us start");

  return HtmlService.createTemplateFromFile('index').evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) 
}
  • .html code

I did not include the whole text, just copy and paste anything you can find

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.11/iframeResizer.contentWindow.min.js" integrity="sha512-FOf4suFgz7OrWmBiyyWW48u/+6GaaAFSDHagh2EBu/GH/1+OQSYc0NFGeGeZK0gZ3vuU1ovmzVzD6bxmT4vayg==" crossorigin="anonymous"></script>
    <h2>Povinnosti identifikované osoby</h2>
     <p>Identifikovaná osoba je povinna <strong>podat přihlášku k&nbsp;registraci do 15 dnů</strong> ode dne, kdy se stala identifikovanou osobou. Tuto přihlášku nemusí na rozdíl od plátců DPH podávat elektronicky. Další povinností identifikované osoby je <strong>vést v&nbsp;evidenci pro účely DPH veškeré údaje</strong>, které se vztahují k&nbsp;jejím daňovým povinnostem.</p>
  </body>
</html>

WordPress files test WP page

  • I created a page and used Custom HTML block.

Inside is this code

<iframe id="test" style=" width: 1px;
    min-width: 100%;" src="https://script.google.com/macros/s/AKfycbyCkz_4-s7HTr4nYirkD_UESfObNLy3-DwE0EpWiemmEkSc1F_kev-UzA/exec"></iframe>
    
 
<script>
 iFrameResize({
                log                     : true,                  // Enable console logging
                enablePublicMethods     : true,                  // Enable methods within iframe hosted page
                resizedCallback         : function(messageData){ // Callback fn when resize is received
                    $('p#callback').html(
                        '<b>Frame ID:</b> '    + messageData.iframe.id +
                        ' <b>Height:</b> '     + messageData.height +
                        ' <b>Width:</b> '      + messageData.width + 
                        ' <b>Event type:</b> ' + messageData.type
                    );
                },
                messageCallback         : function(messageData){ // Callback fn when message is received
                    $('p#callback').html(
                        '<b>Frame ID:</b> '    + messageData.iframe.id +
                        ' <b>Message:</b> '    + messageData.message
                    );
                    alert(messageData.message);
                },
                closedCallback         : function(id){ // Callback fn when iFrame is closed
                    $('p#callback').html(
                        '<b>IFrame (</b>'    + id +
                        '<b>) removed from page.</b>'
                    );
                }
            });
</script>
  • functions.php inside Twenty Twenty-One: Theme Functions

I added this code to include the plugin

/* Custom script with no dependencies, enqueued in the header */
add_action('wp_enqueue_scripts', 'tutsplus_enqueue_custom_js');
function tutsplus_enqueue_custom_js() {
    wp_enqueue_script('custom', 'https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.11/iframeResizer.min.js');
}
Radek
  • 13,813
  • 52
  • 161
  • 255
  • Does [this](https://stackoverflow.com/a/28042315/7215091) help? – Cooper Jan 11 '21 at 02:44
  • Or perhaps [this](https://stackoverflow.com/a/64459189/7215091)? – Cooper Jan 11 '21 at 02:56
  • The first one (setHeight()) did not help but the second looks promising. I have to investigate more. Thank you – Radek Jan 11 '21 at 08:34
  • @TheMaster I added Update2. Is that everything you need to reproduce the issue? – Radek Jan 15 '21 at 12:05
  • Due to [nesting of frames](https://stackoverflow.com/questions/63551837/where-is-my-iframe-in-the-published-web-application-sidebar), I believe a few lines in both the contentWindow.js and the main iframeresizer.js needs to be rewritten to find each other. The parent should postMessage to `window.frames[0].frames[0].frames[0]` and the child to `window.top` – TheMaster Jan 15 '21 at 15:16
  • hmmm, I am not able to do that by myself ;-) – Radek Jan 15 '21 at 15:39

2 Answers2

4

Edit: After chatting with @Radek and @TheMaster it seems the problem is that we need to redirect where the postMessageAPI sends its message to a nested iframe. The inner iframe all have height set to 100% so they will resize once we change the outer frame's height.

Iframe-resizer was never built for this use case, but maybe we can hack it a little. You need to keep the call as you have it now, but if we change line 850 we could redirect the message. Going on what was suggested below I think it would need to change from:

iframe.contentWindow.postMessage(msgId + msg, target)

to something along the lines of:

window.gasFrame.postMessage(msgId + msg, target)

Now we have to get a value for gasFrame. Add the following into your iframe.

top.postMessage('gasFrame', '*')

Then on the main page we need to delay kicking off iframeResize() until we receive that message.

window.addEventListener("message", (event) => {
  if (event.data === 'gasFrame') {
    window.gasFrame = event.source
    iframeResize({ log: true, checkOrigin: false})
  }
}, false)

Our next problem is that browsers tend to under report the height of the content in the iframe by a few pixels. Normally we overcome this by disabling the scroll bars on the iframe. However, as we have a nested iframe that we have no control over we are now unable to do that.

The only solution to this is going to be to add a little bit to the reported height so that reported value is alway more than the actual height, I would expect in most cases around 8px should be ok, but I have seen it out by as much as 16px in the past.

To do this we can change the contentWindow script file as follows at line 1059 you will see the following.

undefined !== customHeight ? customHeight : getHeight[heightCalcMode]()

and change it to

undefined !== customHeight ? customHeight : Number(getHeight[heightCalcMode]()) + 8

As I said above you may need to change 8 to a value that works best for your site.

David Bradshaw
  • 11,859
  • 3
  • 41
  • 70
  • 1
    So, [my answer here](https://stackoverflow.com/questions/63551837/where-is-my-iframe-in-the-published-web-application-sidebar) shows the exact structure of the gas webapp. This webapp itself is embedded in the wordpress site. I also know [postMessage can work in a nested context](https://stackoverflow.com/questions/56400233/issue-with-embedding-google-apps-script-in-an-iframe/56400626). But I'm finding it difficult to point your parent iframe resizer to the `#userHtmlFrame` – TheMaster Jan 14 '21 at 18:20
  • You can pass an array of target frames to the `checkOrigin` option. https://github.com/davidjbradshaw/iframe-resizer/blob/master/docs/parent_page/options.md#checkorigin – David Bradshaw Jan 15 '21 at 10:21
  • Ok, but what do you put in `#myIframe` of `iFrameResize({ log: true }, '#myIframe')` on the parent?. Note that OP doesn't have control over the shallow frame ``script.google.com``, but has control over the deeper nested frame `*.googleusercontent.com`(`#userHtmlFrame`). Also, [this](https://github.com/davidjbradshaw/iframe-resizer/blob/3cb6fce891c92d2c73276b1ae4c5bdc874a5a315/js/iframeResizer.contentWindow.js#L47) should be `window.top` or `window.parent.parent.parent`, I think for the script to work for GAS web app. – TheMaster Jan 15 '21 at 10:38
  • I tried to use `checkOrigin: false` but it did not help. Where are you getting 404 error? I cannot see anything. Is there something I can do, help to debug this? – Radek Jan 15 '21 at 11:29
  • 1
    @TheMaster, instead of '#myIframe', you can pass a direct object reference to the iframe. I've never come across GAS before, but based on your example above you could do something like `iFrameResize({}, window.top.querySelectorAll('iframe')[0])` – David Bradshaw Jan 15 '21 at 16:18
  • Or even `window.top.querySelectorAll('iframe').forEach(iframe => iFrameResize({}, iframe)` – David Bradshaw Jan 15 '21 at 16:19
  • @Radek I see 404 in the top iframe on this http://jsfiddle.net/radek/6qywLr0s/15/. Can you try it in incognito mode, or just log out of Google and then try it – David Bradshaw Jan 15 '21 at 16:21
  • That should most likely be `window.top.document.querySelectorAll('iframe')` in the examples I gave above – David Bradshaw Jan 15 '21 at 16:47
  • Great! Used like `iFrameResize({ log: true ,checkOrigin:false},window.frames[0].frames[0].frames[0])`, it throws `Security error: Permission denied to access property 'tagName' on cross-origin object`. Debugger shows the [chkType function line](https://github.com/davidjbradshaw/iframe-resizer/blob/3cb6fce891c92d2c73276b1ae4c5bdc874a5a315/js/iframeResizer.js#L1362). Same origin policy only allows [limited properties/methods](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#cross-origin_script_api_access) on cross origin iframe. – TheMaster Jan 15 '21 at 17:29
  • @DavidBradshaw please try again. I deployed it again an updated the WP so it works now. – Radek Jan 15 '21 at 18:25
  • @TheMaster all the frames that you traverse like that have to be from the same origin. If your talking cross origin then you have to use the `postMessageAPI` and have the ability to place js code directly onto the page in each domain. – David Bradshaw Jan 16 '21 at 19:24
  • 1
    @TheMaster, going a bit deeper, I have to admit I didn't know you could reach a grand child iframe across multiple domains. The line you point to we could put a try/catch around to stop it erroring, but it is not going to get us much further, because if we can not read `tagName`, then we can also not set `height`. – David Bradshaw Jan 16 '21 at 19:41
  • The second issue is if we dynamically change the height of a grandchild iframe, then we just increase the height of the content in the child iframe. In this case both child and grandchild, and indeed in your example great-grandchild all need to be running iframe-resizer so each level of iframe resizes when it's descendant resizes – David Bradshaw Jan 16 '21 at 19:44
  • 1
    In summary if you have multiple levels of iframes on different domains and you can not add JS to anyone of them, then it is impossible to get this working without the co-operation of the entity that owns the other site. – David Bradshaw Jan 16 '21 at 19:49
  • Well unless you start proxying the site not under you control and inject the required JS, but I don't think that is ethical, it would be outside Googles terms and doubtful legally. – David Bradshaw Jan 16 '21 at 19:52
  • `because if we can not read tagName, then we can also not set height`. That is true of the parent, but Iframeresizer-contentWindow.js runs in the great grand child and it can do what the parent js cannot do. In theory, postMessage can be used to communicate the necessary instructions(At least that's what I thought the library did). About the second issue, I think all three children usually follow the deeper height on the great grandchild(I'm not sure about this, but all the other frames are typically not visible at all. Only the great grandchild has any visible content) – TheMaster Jan 16 '21 at 20:44
  • I probably misunderstood the first issue. `height` could be set on the shallow nested first iframe. But not on the deeper grandchild from the parent. But setting it on the shallow first frame should be enough. I probably need to dive into all your library code to understand better. – TheMaster Jan 16 '21 at 20:49
  • I guess it needs to set ``height`` on `window.frames[0]` based on the content height in `window.frames[0].frames[0].frames[0]`. The great grand child's content is the only thing that matters. The rest of the frames show no visible content(except may be a fixed height for 1 banner on 1 frame(a disclaimer banner from Google)). The rest of the frames are just there for enforcing security. – TheMaster Jan 16 '21 at 21:01
  • @Radek Publish as "Anyone even anonymous" and execute as "Me". – TheMaster Jan 16 '21 at 21:07
  • @Radek Make sure to use the same published url in your jsfiddle. – TheMaster Jan 17 '21 at 05:59
  • 1
    I updated the jsFiddle. it "works" now, sorry for that http://jsfiddle.net/radek/6qywLr0s/23/ I updated the published url everywhere but jsFiddle – Radek Jan 17 '21 at 10:42
  • Now that I see the fiddle I understand what is happening here a bit more, the two inner iframe have height set to `100%`, so we need to get the content size from the inner most iframe and then set it on the outer most. – David Bradshaw Jan 17 '21 at 21:51
  • 1
    This is not something I ever considered before, as it is iframe-resizer posts to the iframe that it is resizing, but it would be possible in theory to change where it the message is sent to. I will add some instructions on what I think you need to do to the answer above – David Bradshaw Jan 17 '21 at 21:53
  • Hi David, the messages are different now. Hopefully you would know https://i.imgur.com/L6fgbxN.png [iFrameSizer][Host page: test] IFrame scrolling disabled for test iframeResizer.js:146:20 [iFrameSizer][Host page: test] [init] Sending msg to iframe[test] (test:8:false:true:32:true:true:null:bodyOffset:null:null:0:false:parent:scroll:true) targetOrigin: https://script.google.com iframeResizer.js:146:20 Uncaught TypeError: iframe.frames is undefined – Radek Jan 20 '21 at 10:32
  • Oh, try `window.frames[0].frames[0].frames[0].contentWindow.postMessage(msgId + msg, target)` instead then – David Bradshaw Jan 20 '21 at 11:33
  • do you want access to the site so it would be easier to debug? https://i.imgur.com/SyLz5Ad.png – Radek Jan 20 '21 at 13:02
  • Can you set this up in jsfiddle, so I can play with it – David Bradshaw Jan 20 '21 at 13:09
  • Also does your app page have more than one iframe? – David Bradshaw Jan 20 '21 at 13:09
  • Reading a bit more you might try `iframe.contentWindow .frames[0].frames[0].postMessage(msgId + msg, target)` – David Bradshaw Jan 20 '21 at 13:10
  • Just need to work out the route through the DOM too that inner iframe – David Bradshaw Jan 20 '21 at 13:12
  • I aimed to have jsFiddle the same like WordPress. The only iframe is I used in html. The rest is from Google. – Radek Jan 20 '21 at 14:04
  • 1
    @Radek Set `checkOrigin:false` as well – TheMaster Jan 20 '21 at 15:22
  • ok, included still getting `Uncaught TypeError: iframe.contentWindow.frames[0]` – Radek Jan 20 '21 at 16:12
  • @TheMaster just trying this out and I'm not convince you can walk child frames like you suggest. As `iframe.contentWindow.frames === iframe.contentWindow` and `iframe.contentWindow.frames[0] === undefined` when I test this the on jsFiddle. – David Bradshaw Jan 20 '21 at 16:17
  • Also `window.frames[0].frames === window.frames[0]` – David Bradshaw Jan 20 '21 at 16:20
  • @Radek Include the whole error message. Also read below. – TheMaster Jan 20 '21 at 16:34
  • I'm not convinced we can walk our way down to the lower iframes in a cross-domain setting, although I would be happy to be proved wrong on that. Another approach would be to have the bottom iframe to do `top.postMessage('GAS iframe', '*')` and then we could use `event.source` on the received message as our target to post to – David Bradshaw Jan 20 '21 at 16:35
  • @DavidBradshaw [REDACTED](that post does the reverse. Rest of the comment is still valid) `window.frames[0].frames === window.frames[0]`. That should be the case. `window.frames===window`. `iframe.contentWindow.frames[0] === undefined` simply means that the first inner frame is not loaded yet. Delay the traversing or use `onload`. – TheMaster Jan 20 '21 at 16:37
  • `iframe.contentWindow.frames[0]` after waiting for iframe to load = `Uncaught DOMException: Blocked a frame with origin "http://fiddle.jshell.net" from accessing a cross-origin frame.` – David Bradshaw Jan 20 '21 at 16:43
  • Updated answer with new idea – David Bradshaw Jan 20 '21 at 17:08
  • FWIW, I don't think `Uncaught DOMException: Blocked a frame with origin "http://fiddle.jshell.net" from accessing a cross-origin frame.` is related to `iframe.contentWindow.frames[0]`. If we could not traverse the frames like that, [this](https://stackoverflow.com/questions/65659485/how-to-change-height-of-gas-iframe-in-wordpress-using-iframe-resizer/65724113?noredirect=1#comment116233853_65724113) error would not have happened(It would've errored out sooner)- I was able to traverse fully. Also same origin policy is clear on the fact `window.frames` read-only property is legal. – TheMaster Jan 20 '21 at 17:19
  • David, sorry I do not know where to put `top.postMessage('gasFrame', '*')` and `window.addEventListener` – Radek Jan 20 '21 at 18:38
  • The `top` one can go anywhere in the iframed page, the `window.addEventListener` can go anywhere on the parent page – David Bradshaw Jan 21 '21 at 09:57
  • `Uncaught TypeError: window.gasFrame is undefined` – Radek Jan 23 '21 at 12:45
  • Are you still calling iframeResize elsewhere? You need to wrap it in the above function now? – David Bradshaw Jan 24 '21 at 14:42
  • 1
    WoW. It is almost perfect. Check the web and search in page source for iframeResizeCall to see what I did. http://rsness.8u.cz/test/ – Radek Jan 28 '21 at 11:42
  • I expect the only option you need in there is `checkOrigin: false` – David Bradshaw Jan 28 '21 at 15:58
  • 1
    My point is that you don't need all the other stuff you have there. You can just use`iframeResize({checkOrigin: false})` – David Bradshaw Jan 28 '21 at 21:15
  • you mean that the callbacks are not needed? It did not work for me. Can we do something about the height? It needs to have few pixels more so the scrollbar disappears. And if I resize the window it is not resizing the iframe. It should be like that? – Radek Jan 29 '21 at 07:37
  • The callbacks are not needed. – David Bradshaw Jan 29 '21 at 10:09
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227994/discussion-between-david-bradshaw-and-radek). – David Bradshaw Jan 29 '21 at 10:09
  • The height thing is tricky, as normally the number that comes back from the iframe can be a few pixels out and we overcome it by disabling scrolling on the iframe. Which of the nested iframe is the scrollbar appearing on? – David Bradshaw Jan 29 '21 at 10:15
  • @Radek updated the answer with how I think it can be fixed – David Bradshaw Jan 30 '21 at 15:49
  • 1
    Hi David, it works now http://rsness.8u.cz/test/ the right value for extra pixels is 19 for me. Thank you for your help. Is there any possibility you can update your plugin so we do not have to modify source code? – Radek Feb 01 '21 at 14:37
  • I'll have a think, but this is very much an edge case – David Bradshaw Feb 01 '21 at 16:26
0

I am no wordpress pro so I am not sure where you add scripts, css and such. But to get this working.

Since I don't have your code I will try go through steps from basics to fix.

  1. Make sure you have the iframeResizer.min.js script included on the wordpress site. (just getting basics out of the way)
  2. As you write in your question make sure that the iframeResizer.contentWindow.min.js is on your google application site. (looks good as you write)
  3. Add small script to initialize like: <script>iFrameResize({ log : true, enablePublicMethods: true});</script> (Log not needed but gives some info to console)

I think you have these basic steps done. This normally should work in base cases but I assume it does not. The most likely issue is how height is calculated for your application. So next try change the heightCalculationMethod property to try different methods of resizing.

For example change your init script to:

<script>iFrameResize({ 
    log : true, 
    enablePublicMethods: true, 
    heightCalculationMethod : 'lowestElement' // This property is added
});</script>

There are multiple options for this property so go through them all and try if any of them work.

  • 'bodyOffset'
  • 'bodyScroll'
  • 'documentElementOffset'
  • 'documentElementScroll'
  • 'max'
  • 'min'
  • 'lowestElement'
  • 'taggedElement'

One of these should update the size correctly.

If this does not work you could try doing the resize manually like this stackoverflow question: make iframe height dynamic based on content inside- JQUERY/Javascript But this might requere updating header with allowed origins and such.

Pls say if this work or I will try update with more help.

JohanSellberg
  • 2,423
  • 1
  • 21
  • 28
  • so I tried all `heightCalculationMethod ` methods and none of them worked. The dynamic based height I tried before posting my question. – Radek Jan 15 '21 at 11:37