3

I have a multi-page Web App. I want after login, a user sees the list of his teammate and marks their attendance status. My issue is I can't show that in an iFrame rather than google script original one.

For instance, I wanted to iFrame it to my own web page. it is two days I can't overcome this issue, I read lots of posts in this regard and also tried another app from scratch, but no luck yet.

For clarification, I am redirecting the user inside the frame using render function. So as @theMaster mentioned it is not a multi-page Web App.

here is my doGet and Render functions:

function doGet(e){
    // Logger.log(e);
    Route.path("login",loadLogin);  
    Route.path("admin",loadAdmin);  
    Route.path("view",loadView);
    Route.path("form",loadForm);
if (e.parameters.id){
  LastID=e.parameters.id[0]; 
}
if (e.parameters.t){
  curT=e.parameters.t[0]; 
}  
if (Route[e.parameters.v]) {
  return Route[e.parameters.v]();
} else {
  return render('W-home');
  }    
}

function render(file, argObject){
    var page = HtmlService.createTemplateFromFile(file);
    if (argObject){
        var keys = Object.keys(argObject);
        keys.forEach(function (k){
        page[k]=argObject[k];
        });
    }
    var evalPage =page.evaluate();
    // if (!xFrame){return evalPage;};
    return evalPage.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
} 

I can go from home page to login page, but after that, even it doesn't let me go to google. In some cases also just returns a blank page inside the frame.

Here is my sample code for the client-side JavaScript.

function goToForm(EmpNo) {
  var link = "<?!=ScriptApp.getService().getUrl()?>" + "?v=form&id=" + EmpNo[1] + "&t=" + EmpNo[2];
  location.href = link;
  // I tried different way to see if i can conqure this! 
  // if (clickBTN= 'a') {
  //   window.open(link, "_self");
  // } if (clickBTN= 'b') {
  //   location.href=link;
  // } if (clickBTN= 'c') {
  //   openNewURLInTheSameWindow(link);
  // }          
}

I also have two global variables xFrame to set XframeOptionsMode to allowAll or not and base to switch base target on the HTM pages to "_top" or "_self".

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Mahhdy
  • 592
  • 9
  • 25

1 Answers1

5

Issue:

You can try

window.top.location = link;

It'll load the link in the top frame. If you are framing the web-app itself in your website, then you should use

window.parent.parent.location = link;

But this won't work because the sandbox prevents navigating other frames in the document. sandbox="allow-top-navigation" only allows top frame navigation and not the intermediate frames. Since the top frame is your site and not script.google.com, it can't be navigated and you'll get the error

Unsafe JavaScript attempt to initiate navigation for frame with URL https://myexamplesite.com... from frame with URL https://*.googleusercontent.com/... The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors.

Solution:

  • Use window.postMessage()
  
==================
|Your site       |<-myexamplesite.com [TOP#0]
|                |
| =============  |
| |GASWebApp  |<-|--script.google.com[Frame#1]
| |           |  |
| |=========  |  |
| ||SandBox|  |  |
| ||User   |<-|--|-- Where your html code is
| ||Frame  |  |  |  (*.googleusercontent.com) 
| |=========  |  |   [Frame#2 and #3(Double nested iframe)]
| |           |  |
| =============  |
|                |
|                |
|                |
==================

Snippet:

Sandboxed Html:

<script>
if(window.parent.parent !== window.top){ //if #1 !== top
  //Framed in a another web-app
  //TODO Use proper target origin instead of *
   window.parent.parent.parent.postMessage("[FRAME#1_LINK_TO_CHANGE]","*");//Post to #0
}
</script>

Top frame(#0):

<!-- Frame the Apps script Web-App Frame#1-->
<iframe
  src="https://script.google.com/macros/s/[SCRIPT_DEPLOYMENT_ID]/exec"
  >Loading...</iframe
>
<script type="text/javascript">
  const handleMessage = function(event) {
    if (
      event.origin !==
      'https://[SCRIPT_ORIGIN]script.googleusercontent.com'
    ) {
      alert('Origin Disallowed');
      return;
    }
      //TODO Check syntax of event.data before changing iframe src
      document.querySelector('iframe').src = event.data; //Change #1 Link
  };
  window.addEventListener('message', handleMessage);
</script>

References:

TheMaster
  • 45,448
  • 6
  • 62
  • 85
  • hi, It jumps out of frame and shows the Google notes on top! but it at least returns something. the other three approach is giving me a blank page! – Mahhdy May 31 '19 at 20:08
  • @Mahhdy You need to remove the third line: `location.href=link;` – TheMaster May 31 '19 at 20:10
  • this was the exact code I had. ` function goToForm(EmpNo){ var link = "=ScriptApp.getService().getUrl()?>"+"?v=form&id="+EmpNo[1]+"&t="+EmpNo[2]; window.top.location =link;` – Mahhdy May 31 '19 at 20:13
  • what do you mean by Top frame? I want the url in my iframe, inside the page not on the parent page. Are we align? – Mahhdy May 31 '19 at 20:15
  • @Mahh `I want the url in my iframe, inside the page not on the parent page`. I see. So, you don't want a multi page application. But a single page application simulating multiple pages. See my answer [here](https://stackoverflow.com/a/54989042). I think it can help. – TheMaster May 31 '19 at 20:22
  • @Mahh Are you using another iframe to load the web-app? The web-app itself loads inside a iframe. In that case, you should use `window.parent.location = link;` – TheMaster May 31 '19 at 20:27
  • by using `windows.parent.location` this error showed up: userCodeAppPanel:83 Unsafe JavaScript attempt to initiate navigation for frame with URL 'https://n-****-script.googleusercontent.com/userCodeAppPanel' from frame with URL 'https://n-2*****-0lu-script.googleusercontent.com/userCodeAppPanel'. The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors. – Mahhdy May 31 '19 at 20:42
  • 1
    @Mah Did you remove location.href=link;. You seem to have a nested iframe both from `*.googleusercontent.com`. If everything is written well, you should have your top frame-``Your site``> iframe>`script.google.com`>iframe-userframe-`*.googleusercontent.com` – TheMaster May 31 '19 at 20:56
  • Thanks of your note. I checked again, really frustrated! I didn't find anything! for test I am just using live server plug in of visual studio code and simple iframe inside of that! other funny things is when I changing the base target to `_self`, i cant check my latest code status and I have to publish each minor change to see that! – Mahhdy May 31 '19 at 21:10
  • @Mahhdy Try setting target as `_parent`. View source code of your app to see the structure of iframes. Search for iframe. Also, You should publish all minor changes. – TheMaster May 31 '19 at 21:31
  • with `_parent` it will open links in a new window and shows them inside google usual page! – Mahhdy May 31 '19 at 21:53
  • Note: *OP confirmed that this works, but I believe mods deleted that "Thank you! It works" comments* – TheMaster Jan 15 '21 at 11:49
  • 1
    seems like iframe [now have it](https://developers.google.com/apps-script/releases#september_1_2021): `the allow-top-navigation-by-user-activation attribute has been added to the sandbox` not sure if it would work without user interaction – Kos Sep 18 '21 at 11:27
  • @Kos That's kinda bad news. They removed `allow-top-navigation` and added `allow-top-navigation-by-user-activation` instead. So, what you could do previously without user activation, now you need user activation. But my script shouldn't be affected in any case - as it uses a top down approach - the external site script is the one changing the link. – TheMaster Sep 18 '21 at 12:48
  • @TheMaster it would be extermly helpful if you provide practical example how to make it work with the on click links. – Abdel5 Nov 12 '21 at 15:01
  • @Abdel5 Feel free to ask a new question. But it's simple as `button.onclick = ()=>window.parent.parent.parent.postMessage("[FRAME#1_LINK_TO_CHANGE]","*");//Post to #0` – TheMaster Nov 12 '21 at 15:09
  • @TheMaster: Probably you could help me with this issue: https://stackoverflow.com/questions/69992538/how-to-navigate-link-to-different-google-sites-from-an-embedded-google-app-scr – Abdel5 Nov 16 '21 at 16:36