This change allows setHTML/getHTML and setText/getText to work on a RichTextArea before the underlying iframe is properly initialized.  It does so by caching the data in a temporary div, and copying the contents into the iframe once its ready.

Suggested by: knorton
Review by: knorton

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1168 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplIE6.java b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplIE6.java
index 4f47335..c186e13 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplIE6.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplIE6.java
@@ -62,25 +62,28 @@
     body.onclick = handler;
   }-*/;
 
+  private static native void setText(Element elem, String text) /*-{
+    elem.contentWindow.document.body.innerText = text;
+  }-*/;
+
   public Element createElement() {
     Element elem = super.createElement();
     DOM.setElementProperty(elem, "src", "javascript:''");
     return elem;
   }
 
-  public String getText() {
-    return getText(elem);
-  }
-
   public native void initElement() /*-{
-    var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
-
+    var _this = this;
     window.setTimeout(function() {
+      var elem = _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
       var doc = elem.contentWindow.document;
       doc.write('<html><body CONTENTEDITABLE="true"></body></html>');
 
       // Initialize event handling.
       @com.google.gwt.user.client.ui.impl.RichTextAreaImplIE6::initEvents(Lcom/google/gwt/user/client/Element;)(elem);
+
+      // Send notification that the iframe has reached design mode.
+      _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImplStandard::onElementInitialized()();
     }, 1);
   }-*/;
 
@@ -89,6 +92,14 @@
     detachEvents(elem);
   }
 
+  protected String getTextImpl() {
+    return getText(elem);
+  }
+
+  protected void setTextImpl(String text) {
+    setText(elem, text);
+  }
+  
   boolean isRichEditingActive(Element elem) {
     return true;
   }
diff --git a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOpera.java b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOpera.java
index 1b2b997..07c0dad 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOpera.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOpera.java
@@ -52,20 +52,6 @@
     return super.getElement();
   }
 
-  public String getHTML() {
-    if (old != null) {
-      return old.getHTML();
-    }
-    return super.getHTML();
-  }
-
-  public String getText() {
-    if (old != null) {
-      return old.getText();
-    }
-    return super.getText();
-  }
-
   public void hookEvents(RichTextArea owner) {
     if (old != null) {
       old.hookEvents(owner);
@@ -105,26 +91,40 @@
     }
   }-*/;
 
-  public void setHTML(String html) {
-    if (old != null) {
-      old.setHTML(html);
-      return;
-    }
-    super.setHTML(html);
-  }
-
-  public void setText(String text) {
-    if (old != null) {
-      old.setText(text);
-      return;
-    }
-    super.setText(text);
-  }
-
   public void unhookEvents() {
     if (old != null) {
       old.unhookEvents();
     }
     super.unhookEvents();
   }
+
+  protected String getHTMLImpl() {
+    if (old != null) {
+      return old.getHTML();
+    }
+    return super.getHTMLImpl();
+  }
+
+  protected String getTextImpl() {
+    if (old != null) {
+      return old.getText();
+    }
+    return super.getTextImpl();
+  }
+
+  protected void setHTMLImpl(String html) {
+    if (old != null) {
+      old.setHTML(html);
+      return;
+    }
+    super.setHTMLImpl(html);
+  }
+
+  protected void setTextImpl(String text) {
+    if (old != null) {
+      old.setText(text);
+      return;
+    }
+    super.setTextImpl(text);
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplStandard.java b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplStandard.java
index 6b9eaab..1d98669 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplStandard.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplStandard.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui.impl;
 
+import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.RichTextArea;
 import com.google.gwt.user.client.ui.RichTextArea.FontSize;
@@ -26,6 +27,12 @@
 public class RichTextAreaImplStandard extends RichTextAreaImpl implements
     RichTextArea.BasicFormatter, RichTextArea.ExtendedFormatter {
 
+  /**
+   * Holds a cached copy of any user setHTML/setText actions until the real
+   * text area is fully initialized.  Becomes <code>null</code> after init.
+   */
+  private Element beforeInitPlaceholder = DOM.createDiv();
+
   public native Element createElement() /*-{
     return $doc.createElement('iframe');
   }-*/;
@@ -42,13 +49,13 @@
     return queryCommandValue("ForeColor");
   }
 
-  public native String getHTML() /*-{
-    return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML;
-  }-*/;
+  public final String getHTML() {
+    return beforeInitPlaceholder == null ? getHTMLImpl() : DOM.getInnerHTML(beforeInitPlaceholder);
+  }
 
-  public native String getText() /*-{
-    return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent;
-  }-*/;
+  public final String getText() {
+    return beforeInitPlaceholder == null ? getTextImpl() : DOM.getInnerText(beforeInitPlaceholder);
+  }
 
   public native void hookEvents(RichTextArea owner) /*-{
     this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.__listener = owner;
@@ -65,6 +72,9 @@
 
       // Initialize event handling.
       _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImplStandard::initEvents()();
+      
+      // Send notification that the iframe has reached design mode.
+      _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImplStandard::onElementInitialized()();
     }, 1);
   }-*/;
 
@@ -160,9 +170,13 @@
     execCommand("ForeColor", color);
   }
 
-  public native void setHTML(String html) /*-{
-    this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML = html;
-  }-*/;
+  public final void setHTML(String html) {
+    if (beforeInitPlaceholder == null) {
+      setHTMLImpl(html);
+    } else {
+      DOM.setInnerHTML(beforeInitPlaceholder, html);
+    }
+  }
 
   public void setJustification(Justification justification) {
     if (justification == Justification.CENTER) {
@@ -174,9 +188,13 @@
     }
   }
 
-  public native void setText(String text) /*-{
-    this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent = text;
-  }-*/;
+  public final void setText(String text) {
+    if (beforeInitPlaceholder == null) {
+      setTextImpl(text);
+    } else {
+      DOM.setInnerText(beforeInitPlaceholder, text);
+    }
+  }
 
   public void toggleBold() {
     execCommand("Bold", "false");
@@ -202,6 +220,22 @@
     execCommand("Underline", "False");
   }
 
+  protected native String getHTMLImpl() /*-{
+    return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML;
+  }-*/;
+
+  protected native String getTextImpl() /*-{
+    return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent;
+  }-*/;
+
+  protected native void setHTMLImpl(String html) /*-{
+    this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML = html;
+  }-*/;
+
+  protected native void setTextImpl(String text) /*-{
+    this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent = text;
+  }-*/;
+
   void execCommand(String cmd, String param) {
     if (isRichEditingActive(elem)) {
       // When executing a command, focus the iframe first, since some commands
@@ -239,6 +273,12 @@
     return ((e.contentWindow.document.designMode).toUpperCase()) == 'ON';
   }-*/;
 
+  void onElementInitialized() {
+    // When the iframe is ready, ensure cached content is set.
+    setHTMLImpl(DOM.getInnerHTML(beforeInitPlaceholder));
+    beforeInitPlaceholder = null;
+  }
+
   boolean queryCommandState(String cmd) {
     if (isRichEditingActive(elem)) {
       // When executing a command, focus the iframe first, since some commands
diff --git a/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java b/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java
new file mode 100644
index 0000000..c4a7953
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/RichTextAreaTest.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 2007 Google Inc.

+ * 

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ * 

+ * http://www.apache.org/licenses/LICENSE-2.0

+ * 

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.user.client.ui;

+

+import com.google.gwt.junit.client.GWTTestCase;

+import com.google.gwt.user.client.Timer;

+

+/**

+ * Tests the {@link RichTextArea} widget.

+ */

+public class RichTextAreaTest extends GWTTestCase {

+

+  public String getModuleName() {

+    return "com.google.gwt.user.User";

+  }

+

+  /**

+   * Test that a delayed set of HTML is reflected. Some platforms have timing

+   * subtleties that need to be tested.

+   */

+  public void testSetHTMLAfterInit() {

+    final RichTextArea richTextArea = new RichTextArea();

+    RootPanel.get().add(richTextArea);

+    new Timer() {

+      public void run() {

+        richTextArea.setHTML("<b>foo</b>");

+        assertEquals("<b>foo</b>", richTextArea.getHTML().toLowerCase());

+        finishTest();

+      }

+    }.schedule(200);

+    delayTestFinish(1000);

+  }

+

+  /**

+   * Test that an immediate set of HTML is reflected immediately and after a

+   * delay. Some platforms have timing subtleties that need to be tested.

+   */

+  public void testSetHTMLBeforeInit() {

+    final RichTextArea richTextArea = new RichTextArea();

+    RootPanel.get().add(richTextArea);

+    richTextArea.setHTML("<b>foo</b>");

+    assertEquals("<b>foo</b>", richTextArea.getHTML().toLowerCase());

+    new Timer() {

+      public void run() {

+        assertEquals("<b>foo</b>", richTextArea.getHTML().toLowerCase());

+        finishTest();

+      }

+    }.schedule(200);

+    delayTestFinish(1000);

+  }

+

+  /**

+   * Test that delayed set of text is reflected. Some platforms have timing

+   * subtleties that need to be tested.

+   */

+  public void testSetTextAfterInit() {

+    final RichTextArea richTextArea = new RichTextArea();

+    RootPanel.get().add(richTextArea);

+    new Timer() {

+      public void run() {

+        richTextArea.setText("foo");

+        assertEquals("foo", richTextArea.getText());

+        finishTest();

+      }

+    }.schedule(200);

+    delayTestFinish(1000);

+  }

+

+  /**

+   * Test that an immediate set of text is reflected immediately and after a

+   * delay. Some platforms have timing subtleties that need to be tested.

+   */

+  public void testSetTextBeforeInit() {

+    final RichTextArea richTextArea = new RichTextArea();

+    RootPanel.get().add(richTextArea);

+    richTextArea.setText("foo");

+    assertEquals("foo", richTextArea.getText());

+    new Timer() {

+      public void run() {

+        assertEquals("foo", richTextArea.getText());

+        finishTest();

+      }

+    }.schedule(200);

+    delayTestFinish(1000);

+  }

+}