diff options
author | Beat Schwarzentrub | 2021-05-06 14:07:25 +0000 |
---|---|---|
committer | Beat Schwarzentrub | 2021-05-06 17:03:45 +0000 |
commit | 700c717d6e03a13f535d2f0b858620fe69095c79 (patch) | |
tree | 07c88f35db25df9c1a3d89d134b0514de3b0da1e | |
parent | c04f796d47624e95a512e2e8d081ef6e6c975da2 (diff) | |
download | org.eclipse.scout.rt-700c717d6e03a13f535d2f0b858620fe69095c79.tar.gz org.eclipse.scout.rt-700c717d6e03a13f535d2f0b858620fe69095c79.tar.xz org.eclipse.scout.rt-700c717d6e03a13f535d2f0b858620fe69095c79.zip |
BrowserField: add method to send messages to the embedded page
- AbstractBrowserField#postMessage:
Send data to an embedded page (iframe)
- AbstractBrowserField#execPostMessage:
Callback that preserves the original form of the received
data, i.e. objects are converted to IDataObject instead of String.
The previous method that converted everything to a String was removed
with 22.0 (see release notes).
- AbstractBrowserField#getConfiguredTrustedMessageOrigins:
New property to specify valid origins. Messages from other origins
are automatically filtered in the UI to prevent sending potentially
malicious data to the server. The default value is an empty list,
which essentially disables this filter (same behavior as before).
270605
Change-Id: I6de030d7af8589e2cba9982c378eea4dfa340d30
Reviewed-on: https://git.eclipse.org/r/c/scout/org.eclipse.scout.rt/+/180316
Tested-by: Scout Bot <scout-bot@eclipse.org>
Reviewed-by: Beat Schwarzentrub <bsh@bsiag.com>
14 files changed, 233 insertions, 51 deletions
diff --git a/eclipse-scout-core/src/form/fields/browserfield/BrowserField.js b/eclipse-scout-core/src/form/fields/browserfield/BrowserField.js index f021236dc7..d817c18ed3 100644 --- a/eclipse-scout-core/src/form/fields/browserfield/BrowserField.js +++ b/eclipse-scout-core/src/form/fields/browserfield/BrowserField.js @@ -23,6 +23,7 @@ export default class BrowserField extends ValueField { this.trackLocation = false; this.sandboxEnabled = true; this.sandboxPermissions = null; + this.trustedMessageOrigins = []; this.scrollBarEnabled = true; this.showInExternalWindow = false; this._messageListener = null; @@ -264,17 +265,28 @@ export default class BrowserField extends ValueField { } _onMessage(event) { + // Only handle event originating form "our" iframe if (event.source !== this.$field[0].contentWindow) { - $.log.isTraceEnabled() && $.log.trace('skipped post-message, because different source. data=' + event.data + ' origin=' + event.origin); return; } - $.log.isDebugEnabled() && $.log.debug('received post-message data=' + event.data + ' origin=' + event.origin); + // Check if the origin is trusted before we do anything else with the data + if (this.trustedMessageOrigins && this.trustedMessageOrigins.length && + !this.trustedMessageOrigins.some(origin => origin === event.origin)) { + $.log.warn('blocked message from untrusted origin ' + event.origin); + return; + } + $.log.isDebugEnabled() && $.log.debug('received post-message: data=' + event.data + ', origin=' + event.origin); this.trigger('message', { data: event.data, origin: event.origin }); } + postMessage(message, targetOrigin) { + $.log.isDebugEnabled() && $.log.debug('send post-message: message=' + message + ', targetOrigin=' + targetOrigin); + this.iframe && this.iframe.postMessage(message, targetOrigin); + } + setTrackLocation(trackLocation) { this.setProperty('trackLocation', trackLocation); this.iframe.setTrackLocation(trackLocation); diff --git a/eclipse-scout-core/src/form/fields/browserfield/BrowserFieldAdapter.js b/eclipse-scout-core/src/form/fields/browserfield/BrowserFieldAdapter.js index 2ef7b3ba19..8564cee5d9 100644 --- a/eclipse-scout-core/src/form/fields/browserfield/BrowserFieldAdapter.js +++ b/eclipse-scout-core/src/form/fields/browserfield/BrowserFieldAdapter.js @@ -40,6 +40,18 @@ export default class BrowserFieldAdapter extends ValueFieldAdapter { } } + _onModelPostMessage(event) { + this.widget.postMessage(event.message, event.targetOrigin); + } + + onModelAction(event) { + if (event.type === 'postMessage') { + this._onModelPostMessage(event); + } else { + super.onModelAction(event); + } + } + _orderPropertyNamesOnSync(newProperties) { // IE won't show scrollbars if the location is set before scrollBarEnabled is set to true. // Rendering the location again after setting the scrollBarEnabled property as done in IFrame.js doesn't seem to work. diff --git a/eclipse-scout-core/src/iframe/IFrame.js b/eclipse-scout-core/src/iframe/IFrame.js index 4e05b7f34e..ef3a576f1a 100644 --- a/eclipse-scout-core/src/iframe/IFrame.js +++ b/eclipse-scout-core/src/iframe/IFrame.js @@ -28,7 +28,7 @@ export default class IFrame extends Widget { } _render() { - var cssClass = 'iframe ' + Device.get().cssClassForIphone(); + let cssClass = 'iframe ' + Device.get().cssClassForIphone(); if (this.wrapIframe) { this.$container = this.$parent.appendDiv('iframe-wrapper'); this.$iframe = this.$container.appendElement('<iframe>', cssClass); @@ -144,4 +144,11 @@ export default class IFrame extends Widget { this._renderLocation(); } } + + postMessage(message, targetOrigin) { + if (!this.rendered) { + return; + } + this.$iframe[0].contentWindow.postMessage(message, targetOrigin); + } } diff --git a/org.eclipse.scout.rt.client/pom.xml b/org.eclipse.scout.rt.client/pom.xml index 2812b4f5bf..22c1a30d39 100644 --- a/org.eclipse.scout.rt.client/pom.xml +++ b/org.eclipse.scout.rt.client/pom.xml @@ -36,5 +36,10 @@ <groupId>org.eclipse.scout.rt</groupId> <artifactId>org.eclipse.scout.rt.shared</artifactId> </dependency> + + <dependency> + <groupId>org.eclipse.scout.rt</groupId> + <artifactId>org.eclipse.scout.rt.dataobject</artifactId> + </dependency> </dependencies> </project> diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/AbstractBrowserFieldExtension.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/AbstractBrowserFieldExtension.java index 0238ef402f..08061dd6e4 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/AbstractBrowserFieldExtension.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/AbstractBrowserFieldExtension.java @@ -22,7 +22,7 @@ public abstract class AbstractBrowserFieldExtension<OWNER extends AbstractBrowse } @Override - public void execPostMessage(BrowserFieldPostMessageChain chain, String data, String origin) { + public void execPostMessage(BrowserFieldPostMessageChain chain, Object data, String origin) { chain.execPostMessage(data, origin); } diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/BrowserFieldChains.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/BrowserFieldChains.java index b1d4749c02..419a1a2133 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/BrowserFieldChains.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/BrowserFieldChains.java @@ -35,7 +35,7 @@ public final class BrowserFieldChains { super(extensions); } - public void execPostMessage(final String data, final String origin) { + public void execPostMessage(final Object data, final String origin) { MethodInvocation<Object> methodInvocation = new MethodInvocation<Object>() { @Override protected void callMethod(IBrowserFieldExtension<? extends AbstractBrowserField> next) { diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/IBrowserFieldExtension.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/IBrowserFieldExtension.java index be37221248..c97a768900 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/IBrowserFieldExtension.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/extension/ui/form/fields/browserfield/IBrowserFieldExtension.java @@ -17,7 +17,7 @@ import org.eclipse.scout.rt.client.ui.form.fields.browserfield.AbstractBrowserFi public interface IBrowserFieldExtension<OWNER extends AbstractBrowserField> extends IFormFieldExtension<OWNER> { - void execPostMessage(BrowserFieldPostMessageChain chain, String data, String origin); + void execPostMessage(BrowserFieldPostMessageChain chain, Object data, String origin); void execExternalWindowStateChanged(BrowserFieldExternalWindowStateChangedChain chain, boolean state); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/AbstractBrowserField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/AbstractBrowserField.java index 2f3d272bb2..cc456cca32 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/AbstractBrowserField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/AbstractBrowserField.java @@ -12,6 +12,7 @@ package org.eclipse.scout.rt.client.ui.form.fields.browserfield; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; @@ -27,6 +28,7 @@ import org.eclipse.scout.rt.client.extension.ui.form.fields.browserfield.Browser import org.eclipse.scout.rt.client.extension.ui.form.fields.browserfield.BrowserFieldChains.BrowserFieldPostMessageChain; import org.eclipse.scout.rt.client.extension.ui.form.fields.browserfield.IBrowserFieldExtension; import org.eclipse.scout.rt.client.ui.form.fields.AbstractFormField; +import org.eclipse.scout.rt.dataobject.IDataObject; import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.Order; import org.eclipse.scout.rt.platform.annotations.ConfigOperation; @@ -92,6 +94,15 @@ public abstract class AbstractBrowserField extends AbstractFormField implements } /** + * @return a list of origin URIs from which this field will receive messages posted via <i>postMessage</i>. If this is + * {@code null} or empty, messages from all origins are accepted. The default is empty. + * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">window.postMessage (MDN)</a> + */ + protected List<String> getConfiguredTrustedMessageOrigins() { + return Collections.emptyList(); + } + + /** * Configures the browser field general behavior. By default the content of the browser field is shown inline or in an * inline container (e.g. an <iframe> for the HTML5 UI layer), some very specific web pages (e.g. using * plug-ins, complex frames within the webpage) might not be displayed well or may even lead to a browser crash. @@ -150,35 +161,36 @@ public abstract class AbstractBrowserField extends AbstractFormField implements } /** - * This callback is invoked when the application has received a post-message from the embedded browser (IFRAME) or - * external window. + * This callback is invoked when the field has received a message from the embedded page ({@code iframe}) or external + * window. * <p> - * The default does nothing. + * The {@code data} can either be a {@link String}, a {@link Number}, a {@link Boolean} or an {@link IDataObject}. * <p> - * <b>Important:</b> this callback is only invoked when the IFRAME is not restricted by the sandbox property. You must - * either disable sandbox completely ({@link #getConfiguredSandboxEnabled()} returns false) or grant the required - * permissions ({@link SandboxPermission#AllowScripts}). + * If {@link #getTrustedMessageOrigins()} is set, the UI should already have checked that the sender is one of the + * trusted origins. However, for security reasons, the {@code origin} should be checked again here. * <p> - * Example java script call (for <iframe>): - * - * <pre> - * window.parent.postMessage('hello application!', 'http://localhost:8082') - * </pre> + * The default does nothing. * <p> - * Other example java script call (for external windows, does not work with all browsers): - * - * <pre> - * window.opener.postMessage('hello application!', 'http://localhost:8082') - * </pre> + * Possible reasons why this method is not called: + * <ul> + * <li>The embedded page use the wrong target {@code window}. + * <li>The browser blocked the message for some unknown reason (check the F12 developer console). + * <li>The sandbox is enabled and does not allow sending messages. + * <li>The embedded page specified the wrong {@code targetOrigin} when calling <i>postMessage</i>. + * <li>The sender origin does not match the list {@link #getTrustedMessageOrigins()}. + * <li>The browser field is disabled. + * </ul> * * @param data + * Message received from the {@code iframe}. Can be a {@link String}, a {@link Number}, a {@link Boolean} or + * an {@link IDataObject} * @param origin - * The origin of the window that sent the message at the time postMessage was called - * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">Window.postMessage()</a> + * The origin of the window that sent the message. + * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">window.postMessage (MDN)</a> */ @ConfigOperation - @Order(260) - protected void execPostMessage(String data, String origin) { + @Order(261) + protected void execPostMessage(Object data, String origin) { } /** @@ -202,6 +214,7 @@ public abstract class AbstractBrowserField extends AbstractFormField implements setScrollBarEnabled(getConfiguredScrollBarEnabled()); setSandboxEnabled(getConfiguredSandboxEnabled()); setSandboxPermissions(getConfiguredSandboxPermissions()); + setTrustedMessageOrigins(getConfiguredTrustedMessageOrigins()); setShowInExternalWindow(getConfiguredShowInExternalWindow()); setExternalWindowButtonText(getConfiguredExternalWindowButtonText()); setExternalWindowFieldText(getConfiguredExternalWindowFieldText()); @@ -347,6 +360,17 @@ public abstract class AbstractBrowserField extends AbstractFormField implements return (Set<BinaryResource>) propertySupport.getProperty(PROP_ATTACHMENTS); } + protected void firePostMessage(Object message, String targetOrigin) { + fireBrowserFieldEvent(new BrowserFieldEvent(this, BrowserFieldEvent.TYPE_POST_MESSAGE) + .withMessage(message) + .withTargetOrigin(targetOrigin)); + } + + @Override + public void postMessage(Object message, String targetOrigin) { + firePostMessage(message, targetOrigin); + } + @Override public void setScrollBarEnabled(boolean scrollBarEnabled) { propertySupport.setProperty(PROP_SCROLL_BAR_ENABLED, scrollBarEnabled); @@ -392,6 +416,16 @@ public abstract class AbstractBrowserField extends AbstractFormField implements propertySupport.setProperty(PROP_SANDBOX_PERMISSIONS, sandboxPermission); } + @Override + public List<String> getTrustedMessageOrigins() { + return propertySupport.getPropertyList(PROP_TRUSTED_MESSAGE_ORIGINS); + } + + @Override + public void setTrustedMessageOrigins(List<String> trustedMessageOrigins) { + propertySupport.setPropertyList(PROP_TRUSTED_MESSAGE_ORIGINS, trustedMessageOrigins); + } + @SuppressWarnings("unchecked") @Override public EnumSet<SandboxPermission> getSandboxPermissions() { @@ -456,7 +490,7 @@ public abstract class AbstractBrowserField extends AbstractFormField implements } @Override - public void firePostMessageFromUI(String data, String origin) { + public void firePostMessageFromUI(Object data, String origin) { if (!isEnabledIncludingParents() || !isVisibleIncludingParents()) { return; } @@ -474,7 +508,7 @@ public abstract class AbstractBrowserField extends AbstractFormField implements } } - protected final void interceptPostMessage(String data, String origin) { + protected final void interceptPostMessage(Object data, String origin) { List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions(); BrowserFieldPostMessageChain chain = new BrowserFieldPostMessageChain(extensions); chain.execPostMessage(data, origin); @@ -493,7 +527,7 @@ public abstract class AbstractBrowserField extends AbstractFormField implements } @Override - public void execPostMessage(BrowserFieldPostMessageChain chain, String data, String origin) { + public void execPostMessage(BrowserFieldPostMessageChain chain, Object data, String origin) { getOwner().execPostMessage(data, origin); } diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/BrowserFieldEvent.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/BrowserFieldEvent.java index 5f0993f51a..2cb75e20ac 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/BrowserFieldEvent.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/BrowserFieldEvent.java @@ -18,10 +18,13 @@ import org.eclipse.scout.rt.client.ui.IModelEvent; public class BrowserFieldEvent extends EventObject implements IModelEvent { private static final long serialVersionUID = 1L; - // state + public static final int TYPE_CONTENT_CHANGED = 900; + public static final int TYPE_POST_MESSAGE = 901; private final int m_type; + private Object m_message; + private String m_targetOrigin; public BrowserFieldEvent(IBrowserField browserField, int type) { super(browserField); @@ -37,6 +40,24 @@ public class BrowserFieldEvent extends EventObject implements IModelEvent { return m_type; } + public Object getMessage() { + return m_message; + } + + public BrowserFieldEvent withMessage(Object message) { + m_message = message; + return this; + } + + public String getTargetOrigin() { + return m_targetOrigin; + } + + public BrowserFieldEvent withTargetOrigin(String targetOrigin) { + m_targetOrigin = targetOrigin; + return this; + } + @Override public String toString() { StringBuilder buf = new StringBuilder(); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserField.java index 4e7eaf365f..497c803d40 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserField.java @@ -11,9 +11,11 @@ package org.eclipse.scout.rt.client.ui.form.fields.browserfield; import java.util.EnumSet; +import java.util.List; import java.util.Set; import org.eclipse.scout.rt.client.ui.form.fields.IFormField; +import org.eclipse.scout.rt.dataobject.IDataObject; import org.eclipse.scout.rt.platform.resource.BinaryResource; import org.eclipse.scout.rt.platform.util.event.IFastListenerList; @@ -63,6 +65,7 @@ public interface IBrowserField extends IFormField { String PROP_SCROLL_BAR_ENABLED = "scrollBarEnabled"; String PROP_SANDBOX_ENABLED = "sandboxEnabled"; String PROP_SANDBOX_PERMISSIONS = "sandboxPermissions"; + String PROP_TRUSTED_MESSAGE_ORIGINS = "trustedMessageOrigins"; String PROP_SHOW_IN_EXTERNAL_WINDOW = "showInExternalWindow"; String PROP_EXTERNAL_WINDOW_FIELD_TEXT = "externalWindowFieldText"; String PROP_EXTERNAL_WINDOW_BUTTON_TEXT = "externalWindowButtonText"; @@ -120,6 +123,21 @@ public interface IBrowserField extends IFormField { */ Set<BinaryResource> getAttachments(); + /** + * Sends a message to the embedded web page ({@code iframe}). + * + * @param message + * The message to send. Can be a {@link String}, a {@link Number}, a {@link Boolean} or an + * {@link IDataObject}. All other objects are ignored. + * @param targetOrigin + * The expected origin of the receiving {@code window}. If the origin does not match, the browser will not + * dispatch the message for security reasons. See the + * <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">documentation</a> for + * details. + * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">window.postMessage (MDN)</a> + */ + void postMessage(Object message, String targetOrigin); + void setScrollBarEnabled(boolean scrollBarEnabled); boolean isScrollBarEnabled(); @@ -178,6 +196,21 @@ public interface IBrowserField extends IFormField { EnumSet<SandboxPermission> getSandboxPermissions(); /** + * @return a list of origin URIs from which this field will receive messages posted via <i>postMessage</i>. If this is + * {@code null} or empty, messages from all origins are accepted. + * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">window.postMessage (MDN)</a> + */ + List<String> getTrustedMessageOrigins(); + + /** + * @param trustedMessageOrigins + * A list of origin URIs from which this field will receive messages posted via <i>postMessage</i>. If this + * is {@code null} or empty, messages from all origins are accepted. + * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">window.postMessage (MDN)</a> + */ + void setTrustedMessageOrigins(List<String> trustedMessageOrigins); + + /** * Configures the browser field general behavior. By default the content of the browser field is shown inline or in an * inline container (e.g. an <iframe> for the HTML5 UI layer), some very specific web pages (e.g. using * plug-ins, complex frames within the webpage) might not be displayed well or may even lead to a browser crash. @@ -188,9 +221,9 @@ public interface IBrowserField extends IFormField { * <p> * Property can only be changed during initialization, it can not be changed during runtime. * - * @param <code>false</code> - * to disable <iframe> usage, <code>true</code> otherwise. - * @see #isShowContentInIFrameEnabled() + * @param showInExternalWindow + * <code>false</code> to disable <iframe> usage, <code>true</code> otherwise. + * @see #isShowInExternalWindow() */ void setShowInExternalWindow(boolean showInExternalWindow); @@ -198,7 +231,7 @@ public interface IBrowserField extends IFormField { * Returns whether content should be shown inline. * * @return <code>false</code> to disable <iframe> usage, <code>true</code> otherwise. - * @see #setShowContentInIFrameEnabled(boolean) + * @see #setShowInExternalWindow(boolean) */ boolean isShowInExternalWindow(); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserFieldUIFacade.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserFieldUIFacade.java index 602fe39431..869958580f 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserFieldUIFacade.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/browserfield/IBrowserFieldUIFacade.java @@ -16,7 +16,7 @@ public interface IBrowserFieldUIFacade { void firePostExternalWindowStateFromUI(boolean windowState); - void firePostMessageFromUI(String data, String origin); + void firePostMessageFromUI(Object data, String origin); BinaryResource requestBinaryResourceFromUI(String filename); diff --git a/org.eclipse.scout.rt.ui.html.test/src/test/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserFieldTest.java b/org.eclipse.scout.rt.ui.html.test/src/test/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserFieldTest.java index 3cd2820527..d399c456ac 100644 --- a/org.eclipse.scout.rt.ui.html.test/src/test/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserFieldTest.java +++ b/org.eclipse.scout.rt.ui.html.test/src/test/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserFieldTest.java @@ -31,28 +31,24 @@ import org.junit.runner.RunWith; */ @RunWith(PlatformTestRunner.class) public class JsonBrowserFieldTest extends BaseFormFieldTest { - /** - * <ul> - * <li>0: origin</li> - * <li>1: data</li> - * </ul> - */ - private String[] m_lastPostMessage; + + private Object m_lastPostMessageData; + private String m_lastPostMessageOrigin; private Boolean m_lastExternalWindowState; private AbstractBrowserField m_model = new AbstractBrowserField() { @Override - protected void execPostMessage(String data, String origin) { - m_lastPostMessage = new String[]{origin, data}; + protected void execPostMessage(Object data, String origin) { + m_lastPostMessageData = data; + m_lastPostMessageOrigin = origin; } @Override protected void execExternalWindowStateChanged(boolean windowState) { m_lastExternalWindowState = windowState; } - }; private JsonBrowserField<IBrowserField> m_browserField = new JsonBrowserField<>(m_model, m_session, m_session.createUniqueId(), new JsonAdapterMock()); @@ -75,9 +71,8 @@ public class JsonBrowserFieldTest extends BaseFormFieldTest { map.put("origin", origin); map.put("data", data); m_browserField.handleUiEvent(new JsonEvent("xyz", "postMessage", new JSONObject(map))); - Assert.assertNotNull(m_lastPostMessage); - Assert.assertEquals(origin, m_lastPostMessage[0]); - Assert.assertEquals(data, m_lastPostMessage[1]); + Assert.assertEquals(data, m_lastPostMessageData); + Assert.assertEquals(origin, m_lastPostMessageOrigin); } @Test @@ -89,5 +84,4 @@ public class JsonBrowserFieldTest extends BaseFormFieldTest { Assert.assertNotNull(m_lastExternalWindowState); Assert.assertEquals(true, m_lastExternalWindowState); } - } diff --git a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserField.java b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserField.java index e946af8dd8..fdc9e157cd 100644 --- a/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserField.java +++ b/org.eclipse.scout.rt.ui.html/src/main/java/org/eclipse/scout/rt/ui/html/json/form/fields/browserfield/JsonBrowserField.java @@ -10,6 +10,8 @@ */ package org.eclipse.scout.rt.ui.html.json.form.fields.browserfield; +import java.util.Collection; +import java.util.List; import java.util.Set; import org.eclipse.scout.rt.client.job.ModelJobs; @@ -17,6 +19,10 @@ import org.eclipse.scout.rt.client.ui.form.fields.browserfield.BrowserFieldEvent import org.eclipse.scout.rt.client.ui.form.fields.browserfield.BrowserFieldListener; import org.eclipse.scout.rt.client.ui.form.fields.browserfield.IBrowserField; import org.eclipse.scout.rt.client.ui.form.fields.browserfield.IBrowserField.SandboxPermission; +import org.eclipse.scout.rt.dataobject.DoList; +import org.eclipse.scout.rt.dataobject.IDataObject; +import org.eclipse.scout.rt.dataobject.IDataObjectMapper; +import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.resource.BinaryResource; import org.eclipse.scout.rt.ui.html.IUiSession; import org.eclipse.scout.rt.ui.html.json.IJsonAdapter; @@ -26,6 +32,7 @@ import org.eclipse.scout.rt.ui.html.json.form.fields.JsonFormField; import org.eclipse.scout.rt.ui.html.res.BinaryResourceHolder; import org.eclipse.scout.rt.ui.html.res.BinaryResourceUrlUtility; import org.eclipse.scout.rt.ui.html.res.IBinaryResourceProvider; +import org.json.JSONArray; import org.json.JSONObject; public class JsonBrowserField<BROWSER_FIELD extends IBrowserField> extends JsonFormField<BROWSER_FIELD> implements IBinaryResourceProvider { @@ -81,6 +88,20 @@ public class JsonBrowserField<BROWSER_FIELD extends IBrowserField> extends JsonF return sb.toString(); } }); + putJsonProperty(new JsonProperty<IBrowserField>(IBrowserField.PROP_TRUSTED_MESSAGE_ORIGINS, model) { + @Override + protected List<String> modelValue() { + return getModel().getTrustedMessageOrigins(); + } + + @Override + public Object prepareValueForToJson(Object value) { + if (value == null) { + return JSONObject.NULL; + } + return new JSONArray((Collection<?>) value); // Do NOT remove the cast! It is required to use the correct constructor. + } + }); putJsonProperty(new JsonProperty<IBrowserField>(IBrowserField.PROP_SHOW_IN_EXTERNAL_WINDOW, model) { @Override protected Boolean modelValue() { @@ -151,10 +172,39 @@ public class JsonBrowserField<BROWSER_FIELD extends IBrowserField> extends JsonF addPropertyChangeEvent(IBrowserField.PROP_LOCATION, getLocation()); } + protected void handleModelPostMessage(Object message, String targetOrigin) { + JSONObject eventData = new JSONObject(); + eventData.put("message", messageToJson(message)); + eventData.put("targetOrigin", targetOrigin); + addActionEvent(EVENT_POST_MESSAGE, eventData); + } + + protected Object messageToJson(Object message) { + if (message == null) { + return JSONObject.NULL; + } + if (message instanceof IDataObject) { + IDataObjectMapper mapper = BEANS.get(IDataObjectMapper.class); + String str = mapper.writeValue(message); + if (message instanceof DoList) { + return new JSONArray(str); + } + return new JSONObject(str); + } + if (message instanceof String || message instanceof Number || message instanceof Boolean) { + return message; + } + // Unsupported (subclasses may override this method to change that) + throw new IllegalArgumentException("Unsupported message type: " + message); + } + protected void handleModelBrowserFieldEvent(BrowserFieldEvent event) { if (BrowserFieldEvent.TYPE_CONTENT_CHANGED == event.getType()) { handleModelContentChanged(); } + else if (BrowserFieldEvent.TYPE_POST_MESSAGE == event.getType()) { + handleModelPostMessage(event.getMessage(), event.getTargetOrigin()); + } } @Override @@ -187,8 +237,20 @@ public class JsonBrowserField<BROWSER_FIELD extends IBrowserField> extends JsonF } protected void handleUiPostMessage(JsonEvent event) { - String data = event.getData().optString("data", null); + Object data = event.getData().opt("data"); String origin = event.getData().optString("origin", null); + + // Support for arbitrary objects (optional support, requires object mapper implementation) + if (data instanceof JSONObject || data instanceof JSONArray) { + // Convert "org.json" object to IDataObject + IDataObjectMapper mapper = BEANS.opt(IDataObjectMapper.class); + if (mapper != null) { + data = mapper.readValue(data.toString(), IDataObject.class); + } + else { + data = data.toString(); + } + } getModel().getUIFacade().firePostMessageFromUI(data, origin); } diff --git a/org.eclipse.scout.rt.ui.html/src/main/resources/org/eclipse/scout/rt/ui/html/json/defaultValues.json b/org.eclipse.scout.rt.ui.html/src/main/resources/org/eclipse/scout/rt/ui/html/json/defaultValues.json index 7b8463e284..fa8a154799 100644 --- a/org.eclipse.scout.rt.ui.html/src/main/resources/org/eclipse/scout/rt/ui/html/json/defaultValues.json +++ b/org.eclipse.scout.rt.ui.html/src/main/resources/org/eclipse/scout/rt/ui/html/json/defaultValues.json @@ -354,7 +354,9 @@ "autoCloseExternalWindow": false, "sandboxEnabled": true, "scrollBarEnabled": false, - "showInExternalWindow": false + "showInExternalWindow": false, + "trustedMessageOrigins": [], + "trackLocation": false }, "BeanField": { "preventInitialFocus": true |