Where is my iframe in the published web application/sidebar?

I've been running into various errors when trying to control the window in DOM of my published web app and addon sidebar.

  • window.location returns a vague unknown url like n-rysutduudd.google-usercontent.com instead of my published web app url script.google.com/[SCRIPT_ID]/exec

  • Attempts to redirect my published web-app to a new url fails window.location.href=www.google.com

  • Forms in web app, when submitted redirects to blank page.

After researching, I figured this is due to the web content being served in a iframe. The documentation doesn't show much except that the webapp is sandboxed in a iframe.

Relevant documentation I've researched:

  • https://developers.google.com/apps-script/migration/iframe
  • https://developers.google.com/apps-script/guides/html/restrictions

Some of the relevant questions I've looked into:

  • window.location.href = window.location.href returning a blank page
  • Google script web app goes blank page after submit
  • How to stop redirected URL from being masked?

My specific question is: Where exactly is my iframe window in the published web app or my add on sidebar?


              PUBLISHED WEB APP
+---------------------------------------------+
|              script.google.com              |
|                                             |<------- [#0] window.top The top frame
|                                             |
|     +---------------------------------+     |
|     |     *.googleusercontent.com     |<----+-------- [#1] Outer Sandboxed Iframe
|     |         sandboxFrame            |     |
|     |    +-----------------------+    |     |
|     |    |        /blank         |    |     |
|     |    |    userHtmlFrame      |    |     |
|     |    |                       |    |     |
|     |    |     Where the html    |<---+-----+-------- [#2] Inner Sandboxed Iframe
|     |    |    file you created   |    |     |
|     |    |       is loaded       |    |     |
|     |    |                       |    |     |
|     |    |                       |    |     |
|     |    |                       |    |     |
|     |    |                       |    |     |
|     |    |                       |    |     |
|     |    +-----------------------+    |     |
|     |                                 |     |
|     |                                 |     |
|     +---------------------------------+     |
|                                             |
|                                             |
|                                             |
+---------------------------------------------+

You're right. Most of the errors are due to the iframe sandboxing done by Google. To answer your question,

  • Your window is located in a iframe with id: userHtmlFrame with it's src set to /blank.

  • This frame is nested inside another frame with src: *.googleusercontent.com and id sandboxFrame.

  • Finally, The sandboxFrame is nested inside the main frame: script.google.com

Notes:

  • window in your published app refers to the inner most frame.

  • This inner most frame has it's own cookies, storage and most other attributes unique to a window.

  • Unfortunately, this inner frame cannot be navigated elsewhere.

  • All window navigation must be done on the outermost frame: script.google.com. This is why the documentation asks you to set base or anchor's target to the top frame.

  • Forms without action are submitted to the inner frame /blank. Therefore, you're redirected to a blank page. The documentation states,

With IFRAME mode however HTML forms are allowed to submit, and if a form element has no action attribute specified it will submit to a blank page. Worse, the inner iframe will redirect to the blank page before the onclick handler has a chance to finish.

  • The origin of your iframe userHtmlFrame is inherited from sandboxFrame and set to *.googleusercontent.com. For all intents and purposes(cors, whitelisting origins, fetch requests), this is the effective origin.

  • The sandboxFrame currently has the following feature policy: allow

accelerometer *; ambient-light-sensor *; autoplay *; camera *; encrypted-media *; fullscreen *; geolocation *; gyroscope *; magnetometer *; microphone *; midi *; payment *; picture-in-picture *; speaker *; usb *; vibrate *; vr *

  • Currently, It has the following sandbox attributes, which limit what you can do to other frames:

allow-downloads allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation-by-user-activation

  • The outer windows/other windows can be referenced using window.top or window.parent or window.opener from the inner frame. But, There are multiple restrictions imposed due to same origin policy. Cross origin access is limited mostly. Of particular note would be the window.postMessage, which allows communication between frames.

            SIDEBAR/MODAL DIALOG
+---------------------------------------------+
|              docs.google.com                |
|  +--------------------------------------+   |<------- [#0] window.top The top frame
|  |      /macros/.../iframedAppPanel     |<--+-------- [#1] Frame1 Same origin 
|  |  +---------------------------------+ |   |
|  |  |     *.googleusercontent.com     |<|---+-------- [#2] Outer Sandboxed Iframe
|  |  |         sandboxFrame            | |   |
|  |  |    +-----------------------+    | |   |
|  |  |    |        /blank         |    | |   |
|  |  |    |    userHtmlFrame      |    | |   |
|  |  |    |                       |    | |   |
|  |  |    |     Where the html    |<---+-|---+-------- [#3] Inner Sandboxed Iframe
|  |  |    |    file you created   |    | |   |
|  |  |    |       is loaded       |    | |   |
|  |  |    |                       |    | |   |
|  |  |    |                       |    | |   |
|  |  |    |                       |    | |   |
|  |  |    |                       |    | |   |
|  |  |    |                       |    | |   |
|  |  |    +-----------------------+    | |   |
|  |  |                                 | |   |
|  |  |                                 | |   |
|  |  +---------------------------------+ |   |
|  |                                      |   |
|  +--------------------------------------+   |
|                                             |
+---------------------------------------------+

All the above notes for web app stands true also for webcontent published using HtmlService in sidebar or modal dialogs. However,

  • There is a extra nested layer of iframe here.
  • The allow-top-navigation is missing from the sandbox attributes. Therefore, It is not possible to change/navigate the top frame(docs.google.com) here.

Changelog:

  • Sep 1, 2021: allow-top-navigation is removed and allow-top-navigation-by-user-activation is added instead. Security for the end user is increased at the cost of convenience for the developers.