blob: 5c2db36202973f8a4a864b611d306dac1968f9a7 [file] [log] [blame]
/*
* Copyright (c) 2014-2018 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
scout.IFrame = function() {
scout.IFrame.parent.call(this);
this.location = null;
this.sandboxEnabled = true;
this.sandboxPermissions = null;
this.scrollBarEnabled = true;
this.trackLocation = false;
// Iframe on iOS is always as big as its content. Workaround it by using a wrapper div with overflow: auto
// Don't wrap it when running in the chrome emulator (in that case isIosPlatform returns false)
this.wrapIframe = scout.device.isIosPlatform();
this.$iframe = null;
this._loadHandler = this._onLoad.bind(this);
this.mutationObserver = null;
};
scout.inherits(scout.IFrame, scout.Widget);
scout.IFrame.prototype._render = function() {
if (this.wrapIframe) {
this.$container = this.$parent.appendDiv('iframe-wrapper');
this.$iframe = this.$container.appendElement('<iframe>', 'iframe');
} else {
this.$iframe = this.$parent.appendElement('<iframe>', 'iframe');
this.$container = this.$iframe;
}
this.htmlComp = scout.HtmlComponent.install(this.$container, this.session);
this.$iframe.one('remove', function() {
if (!this.rendered || this.removing) {
return;
}
this.mutationObserver = new MutationObserver(this._onDomMutation.bind(this));
this.mutationObserver.observe(this.$iframe.document(true), {
subtree: true,
childList: true
});
}.bind(this));
};
scout.IFrame.prototype._remove = function() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
scout.IFrame.parent.prototype._remove.call(this);
};
/**
* @override ValueField.js
*/
scout.IFrame.prototype._renderProperties = function() {
scout.IFrame.parent.prototype._renderProperties.call(this);
this._renderScrollBarEnabled(); // Needs to be before _renderLocation, see comment in _renderScrollBarEnabled
this._renderLocation();
this._renderSandboxEnabled(); // includes _renderSandboxPermissions()
this._renderTrackLocationChange();
};
scout.IFrame.prototype.setLocation = function(location) {
this.setProperty('location', location);
};
scout.IFrame.prototype._renderLocation = function() {
// Convert empty locations to 'about:blank', because in Firefox (maybe others, too?),
// empty locations simply remove the src attribute but don't remove the old content.
var location = this.location || 'about:blank';
this.$iframe.attr('src', location);
};
scout.IFrame.prototype.setTrackLocationChange = function(trackLocation) {
this.setProperty('trackLocation', trackLocation);
};
scout.IFrame.prototype._renderTrackLocationChange = function(trackLocation) {
if (this.trackLocation) {
this.$iframe.on('load', this._loadHandler);
} else {
this.$iframe.off('load', this._loadHandler);
}
};
scout.IFrame.prototype._onDomMutation = function(mutationList) {
mutationList.forEach(function(mutation) {
for (var i = 0; i < mutation.addedNodes.length; i++) {
var elem = mutation.addedNodes[i];
var $elem = $(elem);
if ($elem.isOrHas(this.$iframe)) {
this._onNodeAdded();
}
}
}, this);
};
scout.IFrame.prototype._onDomMutation = function() {
this._renderLocation();
};
scout.IFrame.prototype._onLoad = function(event) {
if (!this.rendered) { // check needed, because this is an async callback
return;
}
if (this.trackLocation) {
var doc = this.$iframe[0].contentDocument;
if (!doc) {
// Doc can be null if website cannot be loaded or if website is not from same origin
return;
}
var location = doc.location.href;
if (location === 'about:blank') {
location = null;
}
this._setProperty('location', location);
}
};
scout.IFrame.prototype.setScrollBarEnabled = function(scrollBarEnabled) {
this.setProperty('scrollBarEnabled', scrollBarEnabled);
};
scout.IFrame.prototype._renderScrollBarEnabled = function() {
this.$container.toggleClass('no-scrolling', !this.scrollBarEnabled);
// According to http://stackoverflow.com/a/18470016, setting 'overflow: hidden' via
// CSS should be enough. However, if the inner page sets 'overflow' to another value,
// scroll bars are shown again. Therefore, we add the legacy 'scrolling' attribute,
// which is deprecated in HTML5, but seems to do the trick.
this.$iframe.attr('scrolling', (this.scrollBarEnabled ? 'yes' : 'no'));
// re-render location otherwise the attribute change would have no effect, see
// https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox
if (this.rendered) {
this._renderLocation();
}
};
scout.IFrame.prototype.setSandboxEnabled = function(sandboxEnabled) {
this.setProperty('sandboxEnabled', sandboxEnabled);
};
scout.IFrame.prototype._renderSandboxEnabled = function() {
if (this.sandboxEnabled) {
this._renderSandboxPermissions();
} else {
this.$iframe.removeAttr('sandbox');
this.$iframe.removeAttr('security');
}
// re-render location otherwise the attribute change would have no effect, see
// https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox
this._renderLocation();
};
scout.IFrame.prototype.setSandboxPermissions = function(sandboxPermissions) {
this.setProperty('sandboxPermissions', sandboxPermissions);
};
scout.IFrame.prototype._renderSandboxPermissions = function() {
if (!this.sandboxEnabled) {
return;
}
this.$iframe.attr('sandbox', scout.nvl(this.sandboxPermissions, ''));
if (scout.device.requiresIframeSecurityAttribute()) {
this.$iframe.attr('security', 'restricted');
}
// re-render location otherwise the attribute change would have no effect, see
// https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-sandbox
this._renderLocation();
};