Adds support to Robot API and client for form buttons. https://reviews.apache.org/r/25034/
diff --git a/src/org/waveprotocol/box/server/robots/operations/DocumentModifyService.java b/src/org/waveprotocol/box/server/robots/operations/DocumentModifyService.java
index 2a501c9..166c360 100644
--- a/src/org/waveprotocol/box/server/robots/operations/DocumentModifyService.java
+++ b/src/org/waveprotocol/box/server/robots/operations/DocumentModifyService.java
@@ -29,6 +29,7 @@
 import com.google.wave.api.Range;
 import com.google.wave.api.data.ApiView;
 import com.google.wave.api.data.DocumentHitIterator;
+import com.google.wave.api.data.ElementSerializer;
 import com.google.wave.api.impl.DocumentModifyAction;
 import com.google.wave.api.impl.DocumentModifyAction.BundledAnnotation;
 import com.google.wave.api.impl.DocumentModifyAction.ModifyHow;
@@ -418,6 +419,9 @@
           // TODO (Yuri Z.) Make it possible to specify a location to insert the
           // gadget and implement insertion at the specified location.
           LineContainers.appendLine(doc, xml);
+        } else if (element.isFormElement()) {
+          XmlStringBuilder xml = ElementSerializer.apiElementToXml(element);
+          LineContainers.appendLine(doc, xml);
         } else {
           // TODO(ljvderijk): Inserting other elements.
           throw new UnsupportedOperationException(
diff --git a/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java b/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java
index be70f0b..206a33b 100644
--- a/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java
+++ b/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java
@@ -30,6 +30,7 @@
 import com.google.wave.api.event.DocumentChangedEvent;
 import com.google.wave.api.event.Event;
 import com.google.wave.api.event.EventType;
+import com.google.wave.api.event.FormButtonClickedEvent;
 import com.google.wave.api.event.WaveletBlipCreatedEvent;
 import com.google.wave.api.event.WaveletBlipRemovedEvent;
 import com.google.wave.api.event.WaveletParticipantsChangedEvent;
@@ -54,6 +55,8 @@
 import org.waveprotocol.wave.model.document.Doc.T;
 import org.waveprotocol.wave.model.document.indexed.DocumentEvent;
 import org.waveprotocol.wave.model.document.indexed.DocumentEvent.AnnotationChanged;
+import org.waveprotocol.wave.model.document.indexed.DocumentEvent.ContentInserted;
+import org.waveprotocol.wave.model.document.raw.impl.Element;
 import org.waveprotocol.wave.model.operation.OperationException;
 import org.waveprotocol.wave.model.operation.SilentOperationSink;
 import org.waveprotocol.wave.model.operation.wave.BasicWaveletOperationContextFactory;
@@ -280,6 +283,21 @@
             addEvent(apiEvent, capabilities, blip.getId(), messages);
           }
         } else {
+          if (capabilities.containsKey(EventType.FORM_BUTTON_CLICKED)) {
+            if (eventComponent.getType() == DocumentEvent.Type.CONTENT_INSERTED) {
+              ContentInserted<N, E, T> contentInserted = (ContentInserted<N, E, T>) eventComponent;
+              Element elementInserted = ((Element) contentInserted.getSubtreeElement());
+              if (elementInserted.getTagName().equals("click")) {
+                FormButtonClickedEvent buttonClickedEvent =
+                    new FormButtonClickedEvent(null, null,
+                        elementInserted.getAttribute("clicker"), Long.decode(elementInserted
+                            .getAttribute("time")), blip.getId(), elementInserted
+                            .getParentElement().getAttribute("name"));
+                addEvent(buttonClickedEvent, capabilities, blip.getId(), messages);
+              }
+            }
+
+          } else
           if (capabilities.containsKey(EventType.DOCUMENT_CHANGED)
               && !documentChangedEventGenerated) {
             DocumentChangedEvent apiEvent = new DocumentChangedEvent(
diff --git a/src/org/waveprotocol/wave/client/doodad/form/button/Button.java b/src/org/waveprotocol/wave/client/doodad/form/button/Button.java
index 7831cd9..e26216c 100644
--- a/src/org/waveprotocol/wave/client/doodad/form/button/Button.java
+++ b/src/org/waveprotocol/wave/client/doodad/form/button/Button.java
@@ -19,6 +19,12 @@
 
 package org.waveprotocol.wave.client.doodad.form.button;
 
+import com.google.gwt.user.client.Event;
+
+import org.waveprotocol.box.webclient.client.Session;
+import org.waveprotocol.wave.client.common.util.DomHelper;
+import org.waveprotocol.wave.client.common.util.DomHelper.HandlerReference;
+import org.waveprotocol.wave.client.common.util.DomHelper.JavaScriptEventListener;
 import org.waveprotocol.wave.client.doodad.form.events.ContentEvents;
 import org.waveprotocol.wave.client.editor.ElementHandlerRegistry;
 import org.waveprotocol.wave.client.editor.NodeEventHandler;
@@ -32,10 +38,14 @@
 import org.waveprotocol.wave.model.document.util.Property;
 import org.waveprotocol.wave.model.document.util.XmlStringBuilder;
 
+import java.util.HashMap;
+
 public final class Button {
   private static final String TAGNAME = "button";
 
   static final Property<ClickButton> BUTTON_LOGIC_PROP = Property.immutable("button_logic");
+  private static final Property<HandlerReference> HANDLE = Property.mutable("handle");
+
 
   private static final ButtonRenderingMutationHandler RENDERING_MUTATION_HANDLER =
       new ButtonRenderingMutationHandler();
@@ -55,6 +65,31 @@
         return false;
       }
     }
+
+    @Override
+    public void onActivated(final ContentElement element) {
+      element.setProperty(HANDLE, DomHelper.registerEventHandler(element.getImplNodelet(), "click",
+          new JavaScriptEventListener() {
+            @Override
+            public void onJavaScriptEvent(String name, Event event) {
+              ContentNode ev = element.getFirstChild();
+              while (ev.getNextSibling() != null && ev.asElement().getName() != "events") {
+                ev = ev.getNextSibling();
+            }
+              HashMap<String, String> attr = new HashMap<String, String>();
+              attr.put("time", Long.toString(System.currentTimeMillis()));
+              attr.put("clicker", Session.get().getAddress());
+              ev.getMutableDoc().createChildElement(ev.asElement(), "click", attr);
+            }
+
+          }));
+    }
+
+    @Override
+    public void onDeactivated(ContentElement element) {
+      // Clean up
+      element.getProperty(HANDLE).unregister();
+    }
   };
 
 
diff --git a/src/org/waveprotocol/wave/client/editor/Editors.java b/src/org/waveprotocol/wave/client/editor/Editors.java
index 0ff2538..0f235ae 100644
--- a/src/org/waveprotocol/wave/client/editor/Editors.java
+++ b/src/org/waveprotocol/wave/client/editor/Editors.java
@@ -26,6 +26,7 @@
 import com.google.gwt.dom.client.Element;
 
 import org.waveprotocol.wave.client.common.util.UserAgent;
+import org.waveprotocol.wave.client.doodad.form.FormDoodads;
 import org.waveprotocol.wave.client.editor.content.ContentDocElement;
 import org.waveprotocol.wave.client.editor.content.ContentDocument;
 import org.waveprotocol.wave.client.editor.content.img.ImgDoodad;
@@ -111,6 +112,8 @@
     AnnotationPaint.register(ROOT_HANDLER_REGISTRY);
     ImgDoodad.register(ROOT_HANDLER_REGISTRY);
 
+    FormDoodads.register(ROOT_HANDLER_REGISTRY);
+
     // after registries, set selection information:
     ValidSelectionStrategy.registerTagForSelections(
         LineContainers.PARAGRAPH_FULL_TAGNAME, false, Skip.NONE);