Vojtech Szocs has uploaded a new change for review.

Change subject: webadmin: Cross-window communication support for UI plugins
......................................................................

webadmin: Cross-window communication support for UI plugins

This patch introduces support for cross-window (cross-origin)
communication that can be utilized by UI plugins, i.e. custom
content (contributed by UI plugin) talks back to UI plugin code
via WebAdmin (parent) window.

This enables custom content to pass arbitrary messages back to
given UI plugin(s) which might, or might not, react to them.

The implementation is based on HTML5 window.postMessage API
that triggers 'message' events on the target (i.e. WebAdmin
parent) window. WebAdmin takes care of intercepting such
messages, doing allowed source origin checks, and passing
message data to UI plugins as appropriate.

Sample UI plugin code:

<snip>

    var api = parent.pluginApi('showcase');

    // New function - define custom API options
    api.options({
        // Can be either a string (single origin)
        // or a string array (multiple origins)
        // "*" means "any origin", as per HTML5 spec
        allowedMessageOrigins: 'http://nicedomain:8080'
    });

    api.register({

        UiInit: function() {
            // Assuming dialog content sends message via:
            //   parent.postMessage(message, targetOrigin)
            api.showDialog(...);
        },

        // New event handler function
        MessageReceived: function(data, sourceWindow) {
            // Source origin check already passed here
            window.alert('MessageReceived: ' + data);
        }

    });

    api.ready();

</snip>

Aside from message processing, 'sourceWindow' can be used
to establish two-way communication between custom content
window and UI plugin code.

This patch also includes smaller (general) improvements.

Change-Id: I8a159bed88e0709b54a31f0959fb08b6cdf508cb
Signed-off-by: Vojtech Szocs <[email protected]>
---
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/SystemModule.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/Plugin.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginEventHandler.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginManager.java
A 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/api/ApiOptions.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsArrayHelper.java
M 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsObjectWithProperties.java
A 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageEventData.java
A 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageReceived.java
A 
frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/PostMessageDispatcher.java
10 files changed, 341 insertions(+), 27 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/94/13794/1

diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/SystemModule.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/SystemModule.java
index 3069b99..1c90ae5 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/SystemModule.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/SystemModule.java
@@ -11,6 +11,7 @@
 import org.ovirt.engine.ui.webadmin.place.WebAdminPlaceManager;
 import org.ovirt.engine.ui.webadmin.system.ApplicationInit;
 import org.ovirt.engine.ui.webadmin.system.InternalConfiguration;
+import org.ovirt.engine.ui.webadmin.system.PostMessageDispatcher;
 
 import com.google.inject.Singleton;
 import com.gwtplatform.mvp.client.proxy.PlaceManager;
@@ -31,6 +32,7 @@
         
bind(PlaceManager.class).to(WebAdminPlaceManager.class).in(Singleton.class);
         bind(ApplicationInit.class).asEagerSingleton();
         bind(InternalConfiguration.class).asEagerSingleton();
+        bind(PostMessageDispatcher.class).asEagerSingleton();
     }
 
     void bindConfiguration() {
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/Plugin.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/Plugin.java
index 253e40b..431ca54 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/Plugin.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/Plugin.java
@@ -3,6 +3,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.ovirt.engine.ui.webadmin.plugin.api.ApiOptions;
 import org.ovirt.engine.ui.webadmin.plugin.jsni.JsFunction;
 
 import com.google.gwt.core.client.JavaScriptObject;
@@ -22,6 +23,9 @@
     // The object containing plugin event handler functions
     private JavaScriptObject eventHandlerObject;
 
+    // The object containing custom plugin API options
+    private ApiOptions apiOptionsObject;
+
     public Plugin(PluginMetaData metaData, IFrameElement iframe) {
         assert metaData != null : "Plugin meta-data cannot be null"; 
//$NON-NLS-1$
         assert iframe != null : "Plugin iframe element cannot be null"; 
//$NON-NLS-1$
@@ -29,10 +33,15 @@
         this.metaData = metaData;
         this.state = PluginState.DEFINED;
         this.iframe = iframe;
+        this.apiOptionsObject = JavaScriptObject.createObject().cast();
     }
 
     public PluginMetaData getMetaData() {
         return metaData;
+    }
+
+    public String getName() {
+        return getMetaData().getName();
     }
 
     public boolean isInState(PluginState state) {
@@ -58,6 +67,14 @@
         return JsFunction.get(eventHandlerObject, functionName);
     }
 
+    public ApiOptions getApiOptionsObject() {
+        return apiOptionsObject;
+    }
+
+    public void setApiOptionsObject(ApiOptions apiOptionsObject) {
+        this.apiOptionsObject = apiOptionsObject;
+    }
+
     /**
      * Verifies if the plugin is currently in one of the given states.
      * <p>
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginEventHandler.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginEventHandler.java
index 67fedea..522fb48 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginEventHandler.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginEventHandler.java
@@ -1,8 +1,11 @@
 package org.ovirt.engine.ui.webadmin.plugin;
 
+import java.util.logging.Logger;
+
 import org.ovirt.engine.ui.common.auth.CurrentUser;
 import org.ovirt.engine.ui.common.auth.UserLoginChangeEvent;
 import 
org.ovirt.engine.ui.common.auth.UserLoginChangeEvent.UserLoginChangeHandler;
+import 
org.ovirt.engine.ui.webadmin.plugin.PluginManager.PluginInvocationCondition;
 import org.ovirt.engine.ui.webadmin.plugin.entity.EntityObject;
 import org.ovirt.engine.ui.webadmin.plugin.jsni.JsArrayHelper;
 import org.ovirt.engine.ui.webadmin.plugin.restapi.RestApiSessionAcquiredEvent;
@@ -21,6 +24,9 @@
 import 
org.ovirt.engine.ui.webadmin.section.main.presenter.tab.TemplateSelectionChangeEvent.TemplateSelectionChangeHandler;
 import 
org.ovirt.engine.ui.webadmin.section.main.presenter.tab.VirtualMachineSelectionChangeEvent;
 import 
org.ovirt.engine.ui.webadmin.section.main.presenter.tab.VirtualMachineSelectionChangeEvent.VirtualMachineSelectionChangeHandler;
+import org.ovirt.engine.ui.webadmin.system.MessageEventData;
+import org.ovirt.engine.ui.webadmin.system.MessageReceivedEvent;
+import 
org.ovirt.engine.ui.webadmin.system.MessageReceivedEvent.MessageReceivedHandler;
 
 import com.google.gwt.event.shared.EventBus;
 import com.google.inject.Inject;
@@ -31,6 +37,8 @@
  * Should be bound as GIN eager singleton, created early on during application 
startup.
  */
 public class PluginEventHandler {
+
+    private static final Logger logger = 
Logger.getLogger(PluginEventHandler.class.getName());
 
     @Inject
     public PluginEventHandler(EventBus eventBus, final PluginManager manager, 
final CurrentUser user) {
@@ -99,6 +107,29 @@
                 manager.invokePluginsNow("VirtualMachineSelectionChange", 
EntityObject.arrayFrom(event.getSelectedItems())); //$NON-NLS-1$
             }
         });
+
+        // Cross-window messaging
+        eventBus.addHandler(MessageReceivedEvent.getType(), new 
MessageReceivedHandler() {
+            @Override
+            public void onMessageReceived(MessageReceivedEvent event) {
+                final MessageEventData eventData = event.getData();
+
+                manager.invokePluginsNowOrLater("MessageReceived", 
//$NON-NLS-1$
+                        JsArrayHelper.createMixedArray(eventData.getData(), 
eventData.getSourceWindow()),
+                        new PluginInvocationCondition() {
+                            @Override
+                            public boolean canInvoke(Plugin plugin) {
+                                if 
(eventData.originMatches(plugin.getApiOptionsObject().getAllowedMessageOrigins()))
 {
+                                    return true;
+                                }
+
+                                logger.info("Plugin [" + plugin.getName() 
//$NON-NLS-1$
+                                        + "] rejected message event for origin 
[" + eventData.getOrigin() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+                                return false;
+                            }
+                        });
+            }
+        });
     }
 
 }
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginManager.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginManager.java
index 568f709..4dbeb8b 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginManager.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/PluginManager.java
@@ -8,6 +8,7 @@
 import java.util.logging.Logger;
 
 import org.ovirt.engine.ui.common.auth.CurrentUser;
+import org.ovirt.engine.ui.webadmin.plugin.api.ApiOptions;
 import org.ovirt.engine.ui.webadmin.plugin.api.PluginUiFunctions;
 import org.ovirt.engine.ui.webadmin.plugin.jsni.JsFunction.ErrorHandler;
 
@@ -36,6 +37,19 @@
  * Should be bound as GIN eager singleton, created early on during application 
startup.
  */
 public class PluginManager {
+
+    public interface PluginInvocationCondition {
+
+        boolean canInvoke(Plugin plugin);
+
+    }
+
+    private static final PluginInvocationCondition INVOKE_ANY_PLUGIN = new 
PluginInvocationCondition() {
+        @Override
+        public boolean canInvoke(Plugin plugin) {
+            return true;
+        }
+    };
 
     private static final Logger logger = 
Logger.getLogger(PluginManager.class.getName());
 
@@ -68,7 +82,7 @@
     }
 
     void addPlugin(Plugin plugin) {
-        plugins.put(plugin.getMetaData().getName(), plugin);
+        plugins.put(plugin.getName(), plugin);
     }
 
     void scheduleFunctionCommand(String pluginName, Command command) {
@@ -152,7 +166,7 @@
      */
     void loadPlugin(Plugin plugin) {
         if (plugin.isInState(PluginState.DEFINED)) {
-            logger.info("Loading plugin [" + plugin.getMetaData().getName() + 
"]"); //$NON-NLS-1$ //$NON-NLS-2$
+            logger.info("Loading plugin [" + plugin.getName() + "]"); 
//$NON-NLS-1$ //$NON-NLS-2$
             Document.get().getBody().appendChild(plugin.getIFrameElement());
             plugin.markAsLoading();
         }
@@ -189,12 +203,22 @@
     /**
      * Invokes an event handler function on all plugins which are currently 
{@linkplain PluginState#IN_USE in use}.
      * <p>
-     * {@code functionArgs} represents the argument list to use when calling 
given function (can be {@code null}).
+     * {@code functionArgs} represents the argument list to use when calling 
the given function (can be {@code null}).
      */
     public void invokePluginsNow(String functionName, JsArray<?> functionArgs) 
{
+        invokePluginsNow(functionName, functionArgs, INVOKE_ANY_PLUGIN);
+    }
+
+    /**
+     * Invokes an event handler function on all plugins which are currently 
{@linkplain PluginState#IN_USE in use} and
+     * meet the given condition.
+     * <p>
+     * {@code functionArgs} represents the argument list to use when calling 
the given function (can be {@code null}).
+     */
+    public void invokePluginsNow(String functionName, JsArray<?> functionArgs, 
PluginInvocationCondition condition) {
         if (canInvokePlugins) {
             for (Plugin plugin : getPlugins()) {
-                if (plugin.isInState(PluginState.IN_USE)) {
+                if (plugin.isInState(PluginState.IN_USE) && 
condition.canInvoke(plugin)) {
                     invokePlugin(plugin, functionName, functionArgs);
                 }
             }
@@ -203,19 +227,31 @@
 
     /**
      * Invokes an event handler function on all plugins which are currently 
{@linkplain PluginState#IN_USE in use}, and
-     * schedules invocation of given function on all plugins that might be put 
in use later on.
+     * schedules invocation of the given function on all plugins that might be 
put in use later on.
      * <p>
-     * {@code functionArgs} represents the argument list to use when calling 
given function (can be {@code null}).
+     * {@code functionArgs} represents the argument list to use when calling 
the given function (can be {@code null}).
      */
-    public void invokePluginsNowOrLater(final String functionName, final 
JsArray<?> functionArgs) {
-        invokePluginsNow(functionName, functionArgs);
+    public void invokePluginsNowOrLater(String functionName, JsArray<?> 
functionArgs) {
+        invokePluginsNowOrLater(functionName, functionArgs, INVOKE_ANY_PLUGIN);
+    }
+
+    /**
+     * Invokes an event handler function on all plugins which are currently 
{@linkplain PluginState#IN_USE in use} and
+     * meet the given condition, and schedules invocation of the given 
function on all plugins that might be put in use
+     * later on.
+     * <p>
+     * {@code functionArgs} represents the argument list to use when calling 
the given function (can be {@code null}).
+     */
+    public void invokePluginsNowOrLater(final String functionName, final 
JsArray<?> functionArgs,
+            final PluginInvocationCondition condition) {
+        invokePluginsNow(functionName, functionArgs, condition);
 
         for (final Plugin plugin : getPlugins()) {
             if (!canInvokePlugins || !plugin.isInState(PluginState.IN_USE)) {
-                scheduleFunctionCommand(plugin.getMetaData().getName(), new 
Command() {
+                scheduleFunctionCommand(plugin.getName(), new Command() {
                     @Override
                     public void execute() {
-                        if (canInvokePlugins && 
plugin.isInState(PluginState.IN_USE)) {
+                        if (canInvokePlugins && 
plugin.isInState(PluginState.IN_USE) && condition.canInvoke(plugin)) {
                             invokePlugin(plugin, functionName, functionArgs);
                         }
                     }
@@ -238,7 +274,7 @@
      * function call.
      */
     boolean invokePlugin(final Plugin plugin, final String functionName, 
JsArray<?> functionArgs) {
-        final String pluginName = plugin.getMetaData().getName();
+        final String pluginName = plugin.getName();
         logger.info("Invoking event handler function [" + functionName + "] 
for plugin [" + pluginName + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
         return 
plugin.getEventHandlerFunction(functionName).invoke(functionArgs, new 
ErrorHandler() {
@@ -275,19 +311,32 @@
     /**
      * Registers an event handler object (object containing plugin event 
handler functions) for the given plugin.
      */
-    void registerPluginEventHandlerObject(String pluginName, JavaScriptObject 
pluginEventHandlerObject) {
+    void registerPluginEventHandlerObject(String pluginName, JavaScriptObject 
eventHandlerObject) {
         Plugin plugin = getPlugin(pluginName);
-        if (plugin == null || pluginEventHandlerObject == null) {
+        if (plugin == null || eventHandlerObject == null) {
             return;
         }
 
         // Allow plugin event handler object to be set only once
         if (plugin.getEventHandlerObject() == null) {
-            plugin.setEventHandlerObject(pluginEventHandlerObject);
+            plugin.setEventHandlerObject(eventHandlerObject);
             logger.info("Plugin [" + pluginName + "] has registered the event 
handler object"); //$NON-NLS-1$ //$NON-NLS-2$
         } else {
             logger.warning("Plugin [" + pluginName + "] has already registered 
the event handler object"); //$NON-NLS-1$ //$NON-NLS-2$
         }
+    }
+
+    /**
+     * Registers a custom API options object for the given plugin.
+     */
+    void registerPluginApiOptionsObject(String pluginName, ApiOptions 
apiOptionsObject) {
+        Plugin plugin = getPlugin(pluginName);
+        if (plugin == null || apiOptionsObject == null) {
+            return;
+        }
+
+        plugin.setApiOptionsObject(apiOptionsObject);
+        logger.info("Plugin [" + pluginName + "] has registered custom API 
options object"); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
     /**
@@ -333,7 +382,7 @@
             return;
         }
 
-        String pluginName = plugin.getMetaData().getName();
+        String pluginName = plugin.getName();
 
         // Try to invoke UiInit event handler function
         if (plugin.isInState(PluginState.READY)) {
@@ -394,8 +443,13 @@
             },
 
             // Registers plugin event handler functions for later invocation
-            register: function(pluginEventHandlerObject) {
-                
[email protected]::registerPluginEventHandlerObject(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this.pluginName,pluginEventHandlerObject);
+            register: function(eventHandlerObject) {
+                
[email protected]::registerPluginEventHandlerObject(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this.pluginName,sanitizeObject(eventHandlerObject));
+            },
+
+            // Registers custom API options object associated with the plugin
+            options: function(apiOptionsObject) {
+                
[email protected]::registerPluginApiOptionsObject(Ljava/lang/String;Lorg/ovirt/engine/ui/webadmin/plugin/api/ApiOptions;)(this.pluginName,sanitizeObject(apiOptionsObject));
             },
 
             // Indicates that the plugin is ready for use
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/api/ApiOptions.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/api/ApiOptions.java
new file mode 100644
index 0000000..f47ffda
--- /dev/null
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/api/ApiOptions.java
@@ -0,0 +1,33 @@
+package org.ovirt.engine.ui.webadmin.plugin.api;
+
+import org.ovirt.engine.ui.webadmin.plugin.jsni.JsObjectWithProperties;
+
+import com.google.gwt.core.client.JsArrayString;
+
+/**
+ * Represents custom API options object associated with the given UI plugin.
+ */
+public final class ApiOptions extends JsObjectWithProperties {
+
+    protected ApiOptions() {
+    }
+
+    /**
+     * Returns allowed origins for which HTML5 {@code message} events should 
be processed.
+     * <p>
+     * The value can be either a string (single origin) or a string array 
(multiple origins).
+     * <p>
+     * Default return value: empty array (reject all {@code message} events)
+     * <p>
+     * Example values:
+     * <ul>
+     * <li>{@code 'http://example.com:8080'} (single origin)
+     * <li>{@code ['http://one.com','https://two.org']} (multiple origins)
+     * <li>"*" (translates to "any origin", as per HTML5 cross-window 
messaging specification)
+     * </ul>
+     */
+    public JsArrayString getAllowedMessageOrigins() {
+        return getValueAsStringArray("allowedMessageOrigins"); //$NON-NLS-1$
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsArrayHelper.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsArrayHelper.java
index 091e018..a3d45d6 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsArrayHelper.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsArrayHelper.java
@@ -23,19 +23,54 @@
     }
 
     /**
+     * Creates a {@link JsArray} containing mixed values.
+     * <p>
+     * Supported value types:
+     * <ul>
+     * <li>{@link JavaScriptObject}, maps to native JS object
+     * <li>String, maps to {@code string}
+     * <li>Double, maps to {@code number}
+     * <li>Boolean, maps to {@code boolean}
+     * </ul>
+     */
+    public static JsArray<JavaScriptObject> createMixedArray(Object... values) 
{
+        JsArray<JavaScriptObject> array = 
JavaScriptObject.createArray().cast();
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                Object obj = values[i];
+                if (obj instanceof JavaScriptObject) {
+                    array.push((JavaScriptObject) obj);
+                } else if (obj instanceof String) {
+                    pushString(array, (String) obj);
+                } else if (obj instanceof Double) {
+                    pushNumber(array, (Double) obj);
+                } else if (obj instanceof Boolean) {
+                    pushBoolean(array, (Boolean) obj);
+                }
+            }
+        }
+        return array;
+    }
+
+    private static native void pushString(JavaScriptObject arrayObj, String 
value) /*-{
+        arrayObj[arrayObj.length] = value;
+    }-*/;
+
+    private static native void pushNumber(JavaScriptObject arrayObj, Double 
value) /*-{
+        arrayObj[arrayObj.length] = value;
+    }-*/;
+
+    private static native void pushBoolean(JavaScriptObject arrayObj, Boolean 
value) /*-{
+        arrayObj[arrayObj.length] = value;
+    }-*/;
+
+    /**
      * Casts the given native array object into {@link JsArray} representation.
      * <p>
      * Returns {@code null} if {@code arrayObj} is not a native array object.
      */
     private static native JsArray<?> toGenericArray(JavaScriptObject arrayObj) 
/*-{
-        return 
(@org.ovirt.engine.ui.webadmin.plugin.jsni.JsArrayHelper::isArray(Lcom/google/gwt/core/client/JavaScriptObject;)(arrayObj))
 ? arrayObj : null;
-    }-*/;
-
-    /**
-     * Returns {@code true} if the given JS object is a native array object.
-     */
-    public static native boolean isArray(JavaScriptObject obj) /*-{
-        return Object.prototype.toString.call(obj) === '[object Array]';
+        return (Object.prototype.toString.call(arrayObj) === '[object Array]') 
? arrayObj : null;
     }-*/;
 
 }
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsObjectWithProperties.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsObjectWithProperties.java
index 8c4bcd7..6a1bf50 100644
--- 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsObjectWithProperties.java
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/jsni/JsObjectWithProperties.java
@@ -2,6 +2,7 @@
 
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.JsArray;
+import com.google.gwt.core.client.JsArrayString;
 
 /**
  * Simple wrapper around a native JS object providing type-safe access to its 
properties.
@@ -34,7 +35,7 @@
      * <p>
      * Returns {@code defaultValue} on missing key, {@code null} value or 
wrong value type.
      */
-    protected native final  Boolean getValueAsBoolean(String key, Boolean 
defaultValue) /*-{
+    protected native final Boolean getValueAsBoolean(String key, Boolean 
defaultValue) /*-{
         return (this[key] != null && typeof this[key] === 'boolean') ? 
@java.lang.Boolean::valueOf(Z)(this[key]) : defaultValue;
     }-*/;
 
@@ -44,7 +45,35 @@
      * Returns {@code defaultValue} on missing key, {@code null} value or 
wrong value type.
      */
     protected native final <T extends JavaScriptObject> JsArray<T> 
getValueAsArray(String key, JsArray<T> defaultValue) /*-{
-        return (this[key] != null && 
@org.ovirt.engine.ui.webadmin.plugin.jsni.JsArrayHelper::isArray(Lcom/google/gwt/core/client/JavaScriptObject;)(this[key]))
 ? this[key] : defaultValue;
+        return (this[key] != null && Object.prototype.toString.call(this[key]) 
=== '[object Array]') ? this[key] : defaultValue;
+    }-*/;
+
+    /**
+     * Returns the value for the given key as native JS array object 
containing String elements only.
+     * <p>
+     * Returns empty array in following situations:
+     * <ul>
+     * <li>missing key, {@code null} value or wrong value type
+     * <li>the underlying array contains no String elements
+     * </ul>
+     * <p>
+     * Returns single-element array if the underlying value is String.
+     */
+    protected native final JsArrayString getValueAsStringArray(String key) /*-{
+        var result = [];
+
+        if (this[key] != null && Object.prototype.toString.call(this[key]) === 
'[object Array]') {
+            for (var i = 0; i < this[key].length; i++) {
+                var element = this[key][i];
+                if (element != null && typeof element === 'string') {
+                    result[result.length] = element;
+                }
+            }
+        } else if (this[key] != null && typeof this[key] === 'string') {
+            result[result.length] = this[key];
+        }
+
+        return result;
     }-*/;
 
     /**
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageEventData.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageEventData.java
new file mode 100644
index 0000000..3bf5b99
--- /dev/null
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageEventData.java
@@ -0,0 +1,55 @@
+package org.ovirt.engine.ui.webadmin.system;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArrayString;
+
+/**
+ * Contains HTML5 {@code message} event data.
+ */
+public class MessageEventData {
+
+    private static final String ANY_ORIGIN = "*"; //$NON-NLS-1$
+
+    // Origin of the window that sent the message
+    private final String origin;
+
+    // Message data, i.e. String or JavaScriptObject instance
+    private final Object data;
+
+    // Window that sent the message
+    private final JavaScriptObject sourceWindow;
+
+    public MessageEventData(String origin, Object data, JavaScriptObject 
sourceWindow) {
+        this.origin = origin;
+        this.data = data;
+        this.sourceWindow = sourceWindow;
+    }
+
+    /**
+     * Returns {@code true} if event origin matches one of {@code 
allowedSourceOrigins}.
+     * <p>
+     * Note that "*" translates to "any origin", as per HTML5 cross-window 
messaging specification.
+     */
+    public boolean originMatches(JsArrayString allowedSourceOrigins) {
+        for (int i = 0; i < allowedSourceOrigins.length(); i++) {
+            String allowedOrigin = allowedSourceOrigins.get(i);
+            if (allowedOrigin != null && (ANY_ORIGIN.equals(allowedOrigin) || 
allowedOrigin.equals(origin))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public String getOrigin() {
+        return origin;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public JavaScriptObject getSourceWindow() {
+        return sourceWindow;
+    }
+
+}
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageReceived.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageReceived.java
new file mode 100644
index 0000000..0828a3d
--- /dev/null
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/MessageReceived.java
@@ -0,0 +1,13 @@
+package org.ovirt.engine.ui.webadmin.system;
+
+import com.gwtplatform.dispatch.annotation.GenEvent;
+
+/**
+ * Event triggered when the application window receives HTML5 {@code message} 
event.
+ */
+@GenEvent
+public class MessageReceived {
+
+    MessageEventData data;
+
+}
diff --git 
a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/PostMessageDispatcher.java
 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/PostMessageDispatcher.java
new file mode 100644
index 0000000..f2a424c
--- /dev/null
+++ 
b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/system/PostMessageDispatcher.java
@@ -0,0 +1,45 @@
+package org.ovirt.engine.ui.webadmin.system;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HasHandlers;
+import com.google.inject.Inject;
+
+/**
+ * Intercepts HTML5 {@code message} events triggered via {@code 
window.postMessage} API.
+ */
+public class PostMessageDispatcher implements HasHandlers {
+
+    private final EventBus eventBus;
+
+    @Inject
+    public PostMessageDispatcher(EventBus eventBus) {
+        this.eventBus = eventBus;
+        registerMessageEventListener();
+    }
+
+    @Override
+    public void fireEvent(GwtEvent<?> event) {
+        eventBus.fireEvent(event);
+    }
+
+    void onMessage(String origin, Object data, JavaScriptObject sourceWindow) {
+        MessageReceivedEvent.fire(this, new MessageEventData(origin, data, 
sourceWindow));
+    }
+
+    private native void registerMessageEventListener() /*-{
+        var context = this;
+
+        // Browsers compliant with postMessage API: 
window.addEventListener('message', callback, false)
+        // IE versions prior to IE9: window.attachEvent('onmessage', callback)
+        var eventListenerMethod = $wnd.addEventListener ? 'addEventListener' : 
'attachEvent';
+        var eventerFunction = $wnd[eventListenerMethod];
+        var messageEventType = (eventListenerMethod == 'attachEvent') ? 
'onmessage' : 'message';
+
+        eventerFunction(messageEventType, function(event) {
+            
conte...@org.ovirt.engine.ui.webadmin.system.PostMessageDispatcher::onMessage(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/core/client/JavaScriptObject;)(event.origin,event.data,event.source);
+        }, false);
+    }-*/;
+
+}


--
To view, visit http://gerrit.ovirt.org/13794
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8a159bed88e0709b54a31f0959fb08b6cdf508cb
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Vojtech Szocs <[email protected]>
_______________________________________________
Engine-patches mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to