Work around dialogs opening on top of CopyableLabels

If the browser has loaded the Flash plugin and used it to display
the CopyableLabel widget from our package it may run into problems
with dialogs displaying over the plugin contet; in many cases the
plugin will draw over the dialog, creating a mashed display that
is not what the designer intended the user to see.

A workaround for Firefox and Chrome is to mark all of the embeds
as visibility=hidden for the duration of the dialog box, and put
them back to their prior visibility setting when the dialog is
dismissed.  By marking them hidden the browser won't dispatch a
paint event to the plugins, and the dialog can draw over them.

Using visibility=hidden is preferred over display=none as it does
not cause the page to relayout when the plugin shows/hides.  This
takes less time for the browser to update the UI when showing or
hiding the dialog, and has less visual impact to the user.  It is
also necessary to work around an apparent bug in WebKit (seen on
both Safari and Chrome) where sometimes the plugin's style was
set to display=block instead of display=inline, causing it to no
longer layout with its siblings the same way.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtexpui/user/User.gwt.xml b/src/main/java/com/google/gwtexpui/user/User.gwt.xml
index 9a94484..ffc2a59 100644
--- a/src/main/java/com/google/gwtexpui/user/User.gwt.xml
+++ b/src/main/java/com/google/gwtexpui/user/User.gwt.xml
@@ -15,4 +15,13 @@
 -->
 <module>
   <inherits name="com.google.gwt.user.User"/>
+
+  <replace-with class="com.google.gwtexpui.user.client.PluginSafeDialogBoxImplAutoHide">
+    <when-type-is class="com.google.gwtexpui.user.client.PluginSafeDialogBoxImpl" />
+    <any>
+      <when-property-is name="user.agent" value="safari"/>
+      <when-property-is name="user.agent" value="gecko"/>
+      <when-property-is name="user.agent" value="gecko1_8"/>
+    </any>
+  </replace-with>
 </module>
diff --git a/src/main/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java b/src/main/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java
index ad28d3a..6da131c 100644
--- a/src/main/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java
+++ b/src/main/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java
@@ -16,17 +16,17 @@
 
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.WindowResizeListener;
-import com.google.gwt.user.client.ui.DialogBox;
 
 /** A DialogBox that automatically re-centers itself if the window changes */
-public class AutoCenterDialogBox extends DialogBox {
+public class AutoCenterDialogBox extends PluginSafeDialogBox {
   private WindowResizeListener recenter;
 
   public AutoCenterDialogBox() {
+    this(false);
   }
 
   public AutoCenterDialogBox(final boolean autoHide) {
-    super(autoHide);
+    this(autoHide, true);
   }
 
   public AutoCenterDialogBox(final boolean autoHide, final boolean modal) {
diff --git a/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBox.java b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBox.java
new file mode 100644
index 0000000..5c885cb
--- /dev/null
+++ b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBox.java
@@ -0,0 +1,65 @@
+// Copyright 2009 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.gwtexpui.user.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.DialogBox;
+
+/**
+ * A DialogBox that can appear over Flash movies and Java applets.
+ * <p>
+ * Some browsers have issues with placing a &lt;div&gt; (such as that used by
+ * the DialogBox implementation) over top of native UI such as that used by the
+ * Flash plugin. Often the native UI leaks over top of the &lt;div&gt;, which is
+ * not the desired behavior for a dialog box.
+ * <p>
+ * This implementation hides the native resources by setting their display
+ * property to 'none' when the dialog is shown, and restores them back to their
+ * prior setting when the dialog is hidden.
+ * */
+public class PluginSafeDialogBox extends DialogBox {
+  private final PluginSafeDialogBoxImpl impl =
+      GWT.create(PluginSafeDialogBoxImpl.class);
+
+  public PluginSafeDialogBox() {
+    this(false);
+  }
+
+  public PluginSafeDialogBox(final boolean autoHide) {
+    this(autoHide, true);
+  }
+
+  public PluginSafeDialogBox(final boolean autoHide, final boolean modal) {
+    super(autoHide, modal);
+  }
+
+  @Override
+  public void setVisible(final boolean show) {
+    impl.visible(show);
+    super.setVisible(show);
+  }
+
+  @Override
+  public void show() {
+    impl.visible(true);
+    super.show();
+  }
+
+  @Override
+  public void hide(final boolean autoClosed) {
+    impl.visible(false);
+    super.hide(autoClosed);
+  }
+}
diff --git a/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImpl.java b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImpl.java
new file mode 100644
index 0000000..bc68d95
--- /dev/null
+++ b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImpl.java
@@ -0,0 +1,20 @@
+// Copyright 2009 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.gwtexpui.user.client;
+
+class PluginSafeDialogBoxImpl {
+  void visible(final boolean dialogVisible) {
+  }
+}
diff --git a/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImplAutoHide.java b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImplAutoHide.java
new file mode 100644
index 0000000..7248fbf
--- /dev/null
+++ b/src/main/java/com/google/gwtexpui/user/client/PluginSafeDialogBoxImplAutoHide.java
@@ -0,0 +1,86 @@
+// Copyright 2009 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.gwtexpui.user.client;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.user.client.ui.UIObject;
+
+import java.util.ArrayList;
+
+class PluginSafeDialogBoxImplAutoHide extends PluginSafeDialogBoxImpl {
+  private boolean hidden;
+  private ArrayList<HiddenElement> hiddenElements =
+      new ArrayList<HiddenElement>();
+
+  @Override
+  void visible(final boolean dialogVisible) {
+    if (dialogVisible) {
+      hideAll();
+    } else {
+      showAll();
+    }
+  }
+
+  private void hideAll() {
+    if (!hidden) {
+      hideSet(Document.get().getElementsByTagName("object"));
+      hideSet(Document.get().getElementsByTagName("embed"));
+      hideSet(Document.get().getElementsByTagName("applet"));
+      hidden = true;
+    }
+  }
+
+  private void hideSet(final NodeList<Element> all) {
+    for (int i = 0; i < all.getLength(); i++) {
+      final Element e = all.getItem(i);
+      if (UIObject.isVisible(e)) {
+        hiddenElements.add(new HiddenElement(e));
+      }
+    }
+  }
+
+  private void showAll() {
+    if (hidden) {
+      for (final HiddenElement e : hiddenElements) {
+        e.restore();
+      }
+      hiddenElements.clear();
+      hidden = false;
+    }
+  }
+
+  private static class HiddenElement {
+    private final Element element;
+    private final String visibility;
+
+    HiddenElement(final Element element) {
+      this.element = element;
+      this.visibility = getVisibility(element);
+      setVisibility(element, "hidden");
+    }
+
+    void restore() {
+      setVisibility(element, visibility);
+    }
+
+    private static native String getVisibility(Element elem)
+    /*-{ return elem.style.visibility; }-*/;
+
+    private static native void setVisibility(Element elem, String disp)
+    /*-{ elem.style.visibility = disp; }-*/;
+  }
+}