Author: b...@google.com
Date: Mon Apr  6 07:07:28 2009
New Revision: 5185

Added:
    trunk/user/src/com/google/gwt/dom/client/StyleInjector.java   (contents,  
props changed)
    trunk/user/test/com/google/gwt/dom/client/StyleInjectorTest.java    
(contents, props changed)
Modified:
    trunk/user/src/com/google/gwt/dom/DOM.gwt.xml
    trunk/user/test/com/google/gwt/dom/DOMSuite.java

Log:
Initial add of StyleInjector.
New StyleInjectorIE implementation based on work by tirsen and reuben.

Patch by: bobv
Review by: rjrjr, cromwellian


Modified: trunk/user/src/com/google/gwt/dom/DOM.gwt.xml
==============================================================================
--- trunk/user/src/com/google/gwt/dom/DOM.gwt.xml       (original)
+++ trunk/user/src/com/google/gwt/dom/DOM.gwt.xml       Mon Apr  6 07:07:28 2009
@@ -44,4 +44,9 @@
      <when-type-is class="com.google.gwt.dom.client.DOMImpl"/>
      <when-property-is name="user.agent" value="gecko"/>
    </replace-with>
+
+  <replace-with  
class="com.google.gwt.dom.client.StyleInjector.StyleInjectorImplIE">
+    <when-type-is  
class="com.google.gwt.dom.client.StyleInjector.StyleInjectorImpl"/>
+    <when-property-is name="user.agent" value="ie6"/>
+  </replace-with>
  </module>

Added: trunk/user/src/com/google/gwt/dom/client/StyleInjector.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/dom/client/StyleInjector.java Mon Apr  6  
07:07:28 2009
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2008 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.dom.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.core.client.JsArrayInteger;
+
+/**
+ * Used to add stylesheets to the document.
+ */
+public class StyleInjector {
+
+  /**
+   * The DOM-compatible way of adding stylesheets. This implementation  
requires
+   * the host HTML page to have a head element defined.
+   */
+  public static class StyleInjectorImpl {
+    private static final StyleInjectorImpl IMPL =  
GWT.create(StyleInjectorImpl.class);
+
+    private HeadElement head;
+
+    public StyleElement injectStyleSheet(String contents) {
+      StyleElement style = createElement(contents);
+      getHead().appendChild(style);
+      return style;
+    }
+
+    public StyleElement injectStyleSheetAtEnd(String contents) {
+      return injectStyleSheet(contents);
+    }
+
+    public StyleElement injectStyleSheetAtStart(String contents) {
+      StyleElement style = createElement(contents);
+      getHead().insertBefore(style, head.getFirstChild());
+      return style;
+    }
+
+    public void setContents(StyleElement style, String contents) {
+      style.setInnerText(contents);
+    }
+
+    private StyleElement createElement(String contents) {
+      StyleElement style = Document.get().createStyleElement();
+      style.setPropertyString("language", "text/css");
+      setContents(style, contents);
+      return style;
+    }
+
+    private HeadElement getHead() {
+      if (head == null) {
+        Element elt =  
Document.get().getElementsByTagName("head").getItem(0);
+        assert elt != null : "The host HTML page does not have a <head>  
element"
+            + " which is required by StyleInjector";
+        head = HeadElement.as(elt);
+      }
+      return head;
+    }
+  }
+
+  /**
+   * IE doesn't allow manipulation of a style element through DOM methods.  
There
+   * is also a hard-coded limit on the number of times that  
createStyleSheet can
+   * be called before IE6 starts throwing exceptions.
+   */
+  public static class StyleInjectorImplIE extends StyleInjectorImpl {
+
+    /*
+     * TODO(bobv) : Talk to scottb about being able to read this out of the
+     * module xml file as a configuration-property to handle cases where
+     * multiple GWT modules are living in the same page.
+     */
+    private static final int MAX_STYLE_SHEETS = 30;
+    private static final JsArray<StyleElement> STYLE_ELEMENTS =  
JavaScriptObject.createArray().cast();
+    private static final JsArrayInteger STYLE_ELEMENT_LENGTHS =  
JavaScriptObject.createArray().cast();
+
+    public native void appendContents(StyleElement style, String contents)  
/*-{
+      style.cssText += contents;
+    }-*/;
+
+    @Override
+    public StyleElement injectStyleSheet(String contents) {
+      int idx = STYLE_ELEMENTS.length();
+      if (idx < MAX_STYLE_SHEETS) {
+        // Just create a new style element and add it to the list
+        StyleElement style = createElement();
+        setContents(style, contents);
+        STYLE_ELEMENTS.set(idx, style);
+        STYLE_ELEMENT_LENGTHS.set(idx, contents.length());
+        return style;
+      } else {
+        /*
+         * Find shortest style element to minimize re-parse time in the  
general
+         * case.
+         */
+        int shortestLen = Integer.MAX_VALUE;
+        int shortestIdx = -1;
+        for (int i = 0; i < idx; i++) {
+          if (STYLE_ELEMENT_LENGTHS.get(i) < shortestLen) {
+            shortestLen = STYLE_ELEMENT_LENGTHS.get(i);
+            shortestIdx = i;
+          }
+        }
+        assert shortestIdx != -1;
+
+        StyleElement style = STYLE_ELEMENTS.get(shortestIdx);
+        STYLE_ELEMENT_LENGTHS.set(shortestIdx, shortestLen +  
contents.length());
+        appendContents(style, contents);
+        return style;
+      }
+    }
+
+    @Override
+    public StyleElement injectStyleSheetAtEnd(String contents) {
+      if (STYLE_ELEMENTS.length() == 0) {
+        return injectStyleSheet(contents);
+      }
+
+      int idx = STYLE_ELEMENTS.length() - 1;
+      StyleElement style = STYLE_ELEMENTS.get(idx);
+      STYLE_ELEMENT_LENGTHS.set(idx, STYLE_ELEMENT_LENGTHS.get(idx)
+          + contents.length());
+      appendContents(style, contents);
+
+      return style;
+    }
+
+    @Override
+    public StyleElement injectStyleSheetAtStart(String contents) {
+      if (STYLE_ELEMENTS.length() == 0) {
+        return injectStyleSheet(contents);
+      }
+
+      StyleElement style = STYLE_ELEMENTS.get(0);
+      STYLE_ELEMENT_LENGTHS.set(0, STYLE_ELEMENT_LENGTHS.get(0)
+          + contents.length());
+      prependContents(style, contents);
+
+      return style;
+    }
+
+    public native void prependContents(StyleElement style, String  
contents) /*-{
+      style.cssText = contents + style.cssText;
+    }-*/;
+
+    @Override
+    public native void setContents(StyleElement style, String contents)  
/*-{
+      style.cssText = contents;
+    }-*/;
+
+    private native StyleElement createElement() /*-{
+      return $doc.createStyleSheet();
+    }-*/;
+  }
+
+  /**
+   * Add a stylesheet to the document. The StyleElement returned by this  
method
+   * is not guaranteed to be unique.
+   *
+   * @param contents the CSS contents of the stylesheet
+   * @return the StyleElement that contains the newly-injected CSS
+   */
+  public static StyleElement injectStylesheet(String contents) {
+    return StyleInjectorImpl.IMPL.injectStyleSheet(contents);
+  }
+
+  /**
+   * Add stylesheet data to the document as though it were declared after  
all
+   * stylesheets previously created by {...@link #injectStylesheet(String)}.  
The
+   * StyleElement returned by this method is not guaranteed to be unique.
+   *
+   * @param contents the CSS contents of the stylesheet
+   * @return the StyleElement that contains the newly-injected CSS
+   */
+  public static StyleElement injectStylesheetAtEnd(String contents) {
+    return StyleInjectorImpl.IMPL.injectStyleSheetAtEnd(contents);
+  }
+
+  /**
+   * Add stylesheet data to the document as though it were declared before  
any
+   * stylesheet previously created by {...@link #injectStylesheet(String)}.  
The
+   * StyleElement returned by this method is not guaranteed to be unique.
+   *
+   * @param contents the CSS contents of the stylesheet
+   * @return the StyleElement that contains the newly-injected CSS
+   */
+  public static StyleElement injectStylesheetAtStart(String contents) {
+    return StyleInjectorImpl.IMPL.injectStyleSheetAtStart(contents);
+  }
+
+  /**
+   * Replace the contents of a previously-injected stylesheet. Updating the
+   * stylesheet in-place is typically more efficient than removing a
+   * previously-created element and adding a new one. This method should  
be used
+   * with some caution as StyleInjector may recycle StyleElements on  
certain
+   * browsers.
+   *
+   * @param style a StyleElement previously-returned from
+   *          {...@link #injectStylesheet(String)}.
+   * @param contents the new contents of the stylesheet.
+   */
+  public static void setContents(StyleElement style, String contents) {
+    StyleInjectorImpl.IMPL.setContents(style, contents);
+  }
+
+  /**
+   * Utility class.
+   */
+  private StyleInjector() {
+  }
+}

Modified: trunk/user/test/com/google/gwt/dom/DOMSuite.java
==============================================================================
--- trunk/user/test/com/google/gwt/dom/DOMSuite.java    (original)
+++ trunk/user/test/com/google/gwt/dom/DOMSuite.java    Mon Apr  6 07:07:28  
2009
@@ -21,6 +21,7 @@
  import com.google.gwt.dom.client.MapTests;
  import com.google.gwt.dom.client.NodeTest;
  import com.google.gwt.dom.client.SelectTests;
+import com.google.gwt.dom.client.StyleInjectorTest;
  import com.google.gwt.dom.client.TableTests;
  import com.google.gwt.junit.tools.GWTTestSuite;

@@ -40,6 +41,7 @@
      suite.addTestSuite(FormTests.class);
      suite.addTestSuite(MapTests.class);
      suite.addTestSuite(SelectTests.class);
+    suite.addTestSuite(StyleInjectorTest.class);
      suite.addTestSuite(TableTests.class);

      return suite;

Added: trunk/user/test/com/google/gwt/dom/client/StyleInjectorTest.java
==============================================================================
--- (empty file)
+++ trunk/user/test/com/google/gwt/dom/client/StyleInjectorTest.java    Mon  
Apr  6 07:07:28 2009
@@ -0,0 +1,88 @@
+/*
+ * 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.gwt.dom.client;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+
+/**
+ * Tests StyleInjector by looking for effects of injected CSS on DOM  
elements.
+ */
+public class StyleInjectorTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.dom.DOMTest";
+  }
+
+  public void testStyleInjector() {
+    final DivElement elt = Document.get().createDivElement();
+    elt.setId("styleInjectorTest");
+    elt.setInnerHTML("Hello StyleInjector!");
+    Document.get().getBody().appendChild(elt);
+
+    StyleInjector.injectStylesheet("#styleInjectorTest {position:  
absolute; left: 100px; width: 50px; height 50px;}");
+    StyleInjector.injectStylesheetAtStart("#styleInjectorTest {left: 25px;  
width: 100px !important;}");
+    StyleInjector.injectStylesheetAtEnd("#styleInjectorTest {height:  
100px;}");
+
+    // We need to allow the document to be redrawn
+    delayTestFinish(500);
+
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        assertEquals(100, elt.getOffsetLeft());
+        assertEquals(100, elt.getClientHeight());
+        assertEquals(100, elt.getClientWidth());
+
+        finishTest();
+      }
+    });
+  }
+
+  /**
+   * Ensure that the IE createStyleSheet compatibility code is exercised.
+   */
+  public void testLotsOfStyles() {
+    StyleElement[] elements = new StyleElement[100];
+    for (int i = 0, j = elements.length; i < j; i++) {
+      elements[i] = StyleInjector.injectStylesheet("#styleInjectorTest" + i
+          + " {position: absolute; left: 100px; width: 50px; height  
50px;}");
+    }
+
+    String id = "styleInjectorTest" + (elements.length - 1);
+    StyleInjector.injectStylesheetAtStart("#" + id
+        + " {left: 25px; width: 100px !important;}");
+    StyleInjector.injectStylesheetAtEnd("#" + id + " {height: 100px;}");
+
+    final DivElement elt = Document.get().createDivElement();
+    elt.setId(id);
+    elt.setInnerHTML("Hello StyleInjector!");
+    Document.get().getBody().appendChild(elt);
+
+    // We need to allow the document to be redrawn
+    delayTestFinish(500);
+
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        assertEquals(100, elt.getOffsetLeft());
+        assertEquals(100, elt.getClientHeight());
+        assertEquals(100, elt.getClientWidth());
+        finishTest();
+      }
+    });
+  }
+}

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to