Fixes race condition in RichTextAreaImplIE6, and badly borked Opera implementation.
Patch by: jgw
Review by: rdayal


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1103 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/RichTextArea.java b/user/src/com/google/gwt/user/client/ui/RichTextArea.java
index c62f15d..cac8406 100644
--- a/user/src/com/google/gwt/user/client/ui/RichTextArea.java
+++ b/user/src/com/google/gwt/user/client/ui/RichTextArea.java
@@ -25,8 +25,8 @@
  * 
  * Because some browsers do not support rich text editing, and others support
  * only a limited subset of functionality, there are two formatter interfaces,
- * accessed via {@link #getBasicFormatter()} and {@link #getExtendedFormatter()}. A
- * browser that does not support rich text editing at all will return
+ * accessed via {@link #getBasicFormatter()} and {@link #getExtendedFormatter()}.
+ * A browser that does not support rich text editing at all will return
  * <code>null</code> for both of these, while one that supports only the basic
  * functionality will return <code>null</code> for the latter.
  * 
@@ -40,7 +40,7 @@
  * </ul>
  */
 public class RichTextArea extends FocusWidget implements HasHTML,
-    SourcesMouseEvents, SourcesChangeEvents {
+    SourcesMouseEvents {
 
   /**
    * This interface is used to access basic formatting options, when available.
@@ -325,7 +325,6 @@
   }
 
   private RichTextAreaImpl impl = (RichTextAreaImpl) GWT.create(RichTextAreaImpl.class);
-  private ChangeListenerCollection changeListeners;
   private MouseListenerCollection mouseListeners;
 
   /**
@@ -336,13 +335,6 @@
     setStyleName("gwt-RichTextArea");
   }
 
-  public void addChangeListener(ChangeListener listener) {
-    if (changeListeners == null) {
-      changeListeners = new ChangeListenerCollection();
-    }
-    changeListeners.add(listener);
-  }
-
   public void addMouseListener(MouseListener listener) {
     if (mouseListeners == null) {
       mouseListeners = new MouseListenerCollection();
@@ -395,25 +387,12 @@
         }
         break;
 
-      case Event.ONCHANGE:
-        // TODO: there's no code to fire this on IE6.
-        if (changeListeners != null) {
-          changeListeners.fireChange(this);
-        }
-        break;
-
       default:
         // ClickEvents and KeyboardEvents
         super.onBrowserEvent(event);
     }
   }
 
-  public void removeChangeListener(ChangeListener listener) {
-    if (changeListeners != null) {
-      changeListeners.remove(listener);
-    }
-  }
-
   public void removeMouseListener(MouseListener listener) {
     if (mouseListeners != null) {
       mouseListeners.remove(listener);
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 85e1d9d..0b8ce57 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
@@ -17,14 +17,14 @@
 
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.RichTextArea;
 
 /**
  * IE6-specific implementation of rich-text editing.
  */
 public class RichTextAreaImplIE6 extends RichTextAreaImplStandard {
 
-  private static native void attachEvents(Element elem) /*-{
+  native void initEvents() /*-{
+    var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
     var handler = function() {
       if (elem.__listener) {
         // Weird: this code has the context of the script frame, but we need the
@@ -33,7 +33,7 @@
         elem.__listener.@com.google.gwt.user.client.ui.RichTextArea::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(evt);
       }
     };
-  
+
     var body = elem.contentWindow.document.body;
     body.onkeydown =
     body.onkeyup =
@@ -63,13 +63,6 @@
     return elem.contentWindow.document.body.innerText;
   }-*/;
 
-  private static native void writeHtml(Element elem) /*-{
-    var doc = elem.contentWindow.document;
-    doc.open();
-    doc.write('<html><body CONTENTEDITABLE="true"></body></html>');
-    doc.close();
-  }-*/;
-
   public Element createElement() {
     Element elem = super.createElement();
     DOM.setElementProperty(elem, "src", "javascript:''");
@@ -80,14 +73,20 @@
     return getText(elem);
   }
 
-  public void hookEvents(RichTextArea owner) {
-    attachEvents(elem);
-    super.hookEvents(owner);
-  }
+  public native void initElement() /*-{
+    var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
+    var _this = this;
 
-  public void initElement() {
-    writeHtml(elem);
-  }
+    window.setTimeout(function() {
+      var doc = elem.contentWindow.document;
+      doc.open();
+      doc.write('<html><body CONTENTEDITABLE="true"></body></html>');
+      doc.close();
+
+      // Initialize event handling.
+      _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImplIE6::initEvents()();
+    }, 1);
+  }-*/;
 
   public void unhookEvents() {
     super.unhookEvents();
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 65587ec..5d3fef0 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
@@ -15,7 +15,6 @@
  */
 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;
 
@@ -24,66 +23,71 @@
  */
 public class RichTextAreaImplOpera extends RichTextAreaImplStandard {
 
-  private static boolean supportsEditing;
+  private static boolean richTextSupported = detectEditingSupport();
+  private static RichTextAreaImpl old;
+
+  static {
+    // If rich text is not supported by this version of Opera, create an
+    // instance of the default impl and shunt all calls to it.
+    if (!richTextSupported) {
+      old = new RichTextAreaImpl();
+    }
+  }
+
+  private static native boolean detectEditingSupport() /*-{
+    return !!$doc.designMode;
+  }-*/;
 
   public Element createElement() {
-    supportsEditing = detectEditingSupport();
-    if (supportsEditing) {
-      return super.createElement();
+    if (old != null) {
+      return old.createElement();
     }
-    return DOM.createTextArea();
+    return super.createElement();
+  }
+
+  public Element getElement() {
+    if (old != null) {
+      return old.getElement();
+    }
+    return super.getElement();
   }
 
   public String getHTML() {
-    if (supportsEditing) {
-      return super.getHTML();
+    if (old != null) {
+      return old.getHTML();
     }
-
-    // THIS DOESN'T WORK, unless there's a compiler bug!
-    return ((RichTextAreaImpl) this).getHTML();
+    return super.getHTML();
   }
 
   public String getText() {
-    if (supportsEditing) {
-      return super.getText();
+    if (old != null) {
+      return old.getText();
     }
-
-    // THIS DOESN'T WORK, unless there's a compiler bug!
-    return ((RichTextAreaImpl) this).getText();
+    return super.getText();
   }
 
   public void hookEvents(RichTextArea owner) {
-    if (supportsEditing) {
-      super.hookEvents(owner);
+    if (old != null) {
+      old.hookEvents(owner);
       return;
     }
-
-    // THIS DOESN'T WORK, unless there's a compiler bug!
-    ((RichTextAreaImpl) this).hookEvents(owner);
+    super.hookEvents(owner);
   }
 
   public void initElement() {
-    if (supportsEditing) {
-      super.initElement();
+    if (old != null) {
+      old.initElement();
       return;
     }
-
-    // THIS DOESN'T WORK, unless there's a compiler bug!
-    ((RichTextAreaImpl) this).initElement();
+    super.initElement();
   }
 
   public boolean isBasicEditingSupported() {
-    return supportsEditing;
+    return richTextSupported;
   }
 
-  public boolean isFullEditingSupported() {
-    return supportsEditing;
-  }
-
-  public void setBackColor(String color) {
-    // Opera uses 'BackColor' for the *entire area's* background. 'HiliteColor'
-    // does what we actually want.
-    execCommand("HiliteColor", color);
+  public boolean isExtendedEditingSupported() {
+    return richTextSupported;
   }
 
   public native void setFocus(boolean focused) /*-{
@@ -96,57 +100,31 @@
   }-*/;
 
   public void setHTML(String html) {
-    if (supportsEditing) {
-      super.setHTML(html);
+    if (old != null) {
+      old.setHTML(html);
+      return;
     }
-
-    // Unsupported fallback.
-    DOM.setElementProperty(elem, "value", html);
+    super.setHTML(html);
   }
 
   public void setText(String text) {
-    if (supportsEditing) {
-      super.setText(text);
+    if (old != null) {
+      old.setText(text);
+      return;
     }
-
-    // Unsupported fallback.
-    DOM.setElementProperty(elem, "value", text);
+    super.setText(text);
   }
 
   public void unhookEvents() {
-    if (supportsEditing) {
-      super.unhookEvents();
-      return;
+    if (old != null) {
+      old.unhookEvents();
     }
-
-    // THIS DOESN'T WORK, unless there's a compiler bug!
-    ((RichTextAreaImpl) this).unhookEvents();
+    super.unhookEvents();
   }
 
-  void execCommand(String cmd, String param) {
-    if (isRichEditingActive(elem)) {
-      // Opera doesn't need focus for execCommand() to work, but focusing
-      // the editor causes it to lose its selection, so we focus *after*
-      // execCommand().
-      execCommandAssumingFocus(cmd, param);
-      setFocus(true);
-
-      // TODO: Opera has now lost its selection. Figure out a way to restore it
-      // reliably.
-    }
+  public void setBackColor(String color) {
+    // Opera uses 'BackColor' for the *entire area's* background. 'HiliteColor'
+    // does what we actually want.
+    execCommand("HiliteColor", color);
   }
-
-  boolean queryCommandState(String cmd) {
-    // Opera doesn't need focus (and setting it dumps selection).
-    return queryCommandStateAssumingFocus(cmd);
-  }
-
-  String queryCommandValue(String cmd) {
-    // Opera doesn't need focus (and setting it dumps selection).
-    return queryCommandValueAssumingFocus(cmd);
-  }
-
-  private native boolean detectEditingSupport() /*-{
-    return !!$doc.designMode;
-  }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplSafari.java b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplSafari.java
index 5569548..bc7695d 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplSafari.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplSafari.java
@@ -54,7 +54,7 @@
   }-*/;
 
   public native void setFocus(boolean focused) /*-{
-    // Opera needs the *iframe* focused, not its window.
+    // Safari needs the *iframe* focused, not its window.
     if (focused) {
       this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.focus();
       this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.__gwt_restoreSelection();