Apps Script uses a security sandbox
to provide protective isolation for Google Workspace
applications in certain situations. All
sandbox modes are now sunset except for IFRAME
. Apps using older sandbox
modes now use the newer IFRAME
mode automatically.
Apps that previously used these older modes with the HTML Service may need to
make changes for IFRAME
mode, to address the following differences:
- You now must override the link's
target
attribute usingtarget="_top"
ortarget="_blank"
- HTML files served by the HTML Service must include <!DOCTYPE html>, <html>, and <body> tags
- The Google native loader library
api.js
doesn't load automatically inIFRAME
mode - Picker users need to call
setOrigin()
because content is served from a new domain - Some older browsers, including IE9, are not supported
- Imported resources must now use HTTPS
- Form submission are no longer prevented by default
These differences are detailed in the following sections.
Setting the link target attribute
In the IFRAME
mode you need to set the link target attribute to either _top
or _blank
:
Code.js
function doGet() {
var template = HtmlService.createTemplateFromFile('top');
return template.evaluate();
}
top.html
<!DOCTYPE html>
<html>
<body>
<div>
<a href="http://google.com" target="_top">Click Me!</a>
</div>
</body>
</html>
You can also override this attribute using the <base> tag within the head section of the enclosing web page:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div>
<a href="http://google.com">Click Me!</a>
</div>
</body>
</html>
Top-level HTML tags
Under NATIVE
(and EMULATED
) sandbox mode, certain HTML tags would be
automatically added to an Apps Script .html file, but this does not happen when
using IFRAME
mode.
To make sure your project pages are served correctly using IFRAME
, wrap your
page content in the following top-level tags:
<!DOCTYPE html>
<html>
<body>
<!-- Add your HTML content here -->
</body>
</html>
Native Javascript loader library must be explicitly loaded
Scripts that relied on automatic loading of the native loader library api.js
must be changed to load this library explicitly, as in the following example:
<script src="https://apis.google.com/js/api.js?onload=onApiLoad">
</script>
Google Picker API change
When using the Google Picker API, you must now call setOrigin()
when constructing
the PickerBuilder and pass in the origin google.script.host.origin
, as shown
in the following example:
function createPicker(oauthToken) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.SPREADSHEETS) // Or a different ViewId
.setOAuthToken(oauthToken)
.setDeveloperKey(developerKey)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin) // Note the setOrigin
.build();
picker.setVisible(true);
}
For a full working example, see File-open dialogs.
Browser support
The IFRAME
sandbox mode is based on the
iframe sandboxing
feature in HTML5.
This is not supported in some older browsers, such as Internet Explorer 9. This
can be an issue if your Apps Script project both:
- uses
HtmlService
, and - previously used
EMULATED
orNATIVE
sandboxing
Migrating these apps to IFRAME
sandbox mode means they may no longer work on
some older browsers (notably IE9 and earlier) that don't support HTML5's iframe
sandboxing feature.
Apps that already request IFRAME
mode or don't use HtmlService
at all are
unaffected by this issue.
HTTPS is now required for imported resources
Previous applications that imported resources using HTTP must be changed to use HTTPS instead.
Form submission are no longer prevented by default
Under NATIVE
sandboxing HTML forms were prevented from actually submitting
and navigating the page. Given that, a developer could simply add an onclick
handler to the submit button and not have to worry about what happened after.
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 solution is to add JavaScript code to your page that prevents the form elements from actually submitting, so that the click handlers have time to function:
<script>
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
</script>
A complete example can be found on the HtmlService guide Client-to-Server Communication.