All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dom.client.StyleInjector Maven / Gradle / Ivy

There is a newer version: 2.8.2-v20191108
Show newest version
/*
 * 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.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;

/**
 * Used to add stylesheets to the document. The one-argument versions of
 * {@link #inject}, {@link #injectAtEnd}, and {@link #injectAtStart} use
 * {@link Scheduler#scheduleFinally} to minimize the number of individual style
 * elements created.
 * 

* The api here is a bit redundant, with similarly named methods returning * either void or {@link StyleElement} — e.g., * {@link #inject(String) void inject(String)} v. * {@link #injectStylesheet(String) StyleElement injectStylesheet(String)}. The * methods that return {@link StyleElement} are not guaranteed to work as * expected on Internet Explorer. Because they are still useful to developers on * other browsers they are not deprecated, but IE developers should * avoid the methods with {@link StyleElement} return values (at least * up until, and excluding, IE10). */ 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 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 IE8-9 starts throwing exceptions. */ public static class StyleInjectorImplIE extends StyleInjectorImpl { /** * The maximum number of style tags that can be handled by IE. */ private static final int MAX_STYLE_SHEETS = 31; /** * A cache of the lengths of the current style sheets. A value of 0 * indicates that the length has not yet been retrieved. */ private static int[] styleSheetLengths = new int[MAX_STYLE_SHEETS]; private static native int getDocumentStyleCount() /*-{ return $doc.styleSheets.length; }-*/; private static native StyleElement getDocumentStyleSheet(int index) /*-{ return $doc.styleSheets[index]; }-*/; private static native int getDocumentStyleSheetLength(int index) /*-{ return $doc.styleSheets[index].cssText.length; }-*/; public native void appendContents(StyleElement style, String contents) /*-{ style.cssText += contents; }-*/; @Override public StyleElement injectStyleSheet(String contents) { int numStyles = getDocumentStyleCount(); if (numStyles < MAX_STYLE_SHEETS) { // Just create a new style element and add it to the list return createNewStyleSheet(contents); } else { /* * Find shortest style element to minimize re-parse time in the general * case. * * We cache the lengths of the style sheets in order to avoid expensive * calls to retrieve their actual contents. Note that if another module * or script makes changes to the style sheets that we are unaware of, * the worst that will happen is that we will choose a style sheet to * append to that is not actually of minimum size. * * We also play safe by counting only the MAX_STYLE_SHEETS first style * sheets, just in case the limits are raised somehow (e.g. if this * implementation is used in IE10 which removes --or significantly * raises-- the limits.) */ int shortestLen = Integer.MAX_VALUE; int shortestIdx = -1; for (int i = 0; i < MAX_STYLE_SHEETS; i++) { int len = styleSheetLengths[i]; if (len == 0) { // Cache the length len = styleSheetLengths[i] = getDocumentStyleSheetLength(i); } if (len <= shortestLen) { shortestLen = len; shortestIdx = i; } } styleSheetLengths[shortestIdx] += contents.length(); return appendToStyleSheet(shortestIdx, contents, true); } } @Override public StyleElement injectStyleSheetAtEnd(String contents) { int documentStyleCount = getDocumentStyleCount(); if (documentStyleCount == 0) { return createNewStyleSheet(contents); } return appendToStyleSheet(documentStyleCount - 1, contents, true); } @Override public StyleElement injectStyleSheetAtStart(String contents) { if (getDocumentStyleCount() == 0) { return createNewStyleSheet(contents); } return appendToStyleSheet(0, contents, false); // prepend } public native void prependContents(StyleElement style, String contents) /*-{ style.cssText = contents + style.cssText; }-*/; private StyleElement appendToStyleSheet(int idx, String contents, boolean append) { StyleElement style = getDocumentStyleSheet(idx); if (append) { appendContents(style, contents); } else { prependContents(style, contents); } return style; } private native StyleElement createElement() /*-{ return $doc.createStyleSheet(); }-*/; private StyleElement createNewStyleSheet(String contents) { StyleElement style = createElement(); style.setCssText(contents); return style; } } private static final JsArrayString toInject = JavaScriptObject.createArray().cast(); private static final JsArrayString toInjectAtEnd = JavaScriptObject.createArray().cast(); private static final JsArrayString toInjectAtStart = JavaScriptObject.createArray().cast(); private static ScheduledCommand flusher = new ScheduledCommand() { public void execute() { if (needsInjection) { flush(null); } } }; private static boolean needsInjection = false; /** * Flushes any pending stylesheets to the document. *

* This can be useful if you used CssResource.ensureInjected but * now in the same event loop want to measure widths based on the * new styles. *

* Note that calling this method excessively will decrease performance. */ public static void flush() { inject(true); } /** * Add a stylesheet to the document. * * @param css the CSS contents of the stylesheet */ public static void inject(String css) { inject(css, false); } /** * Add a stylesheet to the document. * * @param css the CSS contents of the stylesheet * @param immediate if true the DOM will be updated immediately * instead of just before returning to the event loop. Using this * option excessively will decrease performance, especially if used * with an inject-css-on-init coding pattern */ public static void inject(String css, boolean immediate) { toInject.push(css); inject(immediate); } /** * Add stylesheet data to the document as though it were declared after all * stylesheets previously created by {@link #inject(String)}. * * @param css the CSS contents of the stylesheet */ public static void injectAtEnd(String css) { injectAtEnd(css, false); } /** * Add stylesheet data to the document as though it were declared after all * stylesheets previously created by {@link #inject(String)}. * * @param css the CSS contents of the stylesheet * @param immediate if true the DOM will be updated immediately * instead of just before returning to the event loop. Using this * option excessively will decrease performance, especially if used * with an inject-css-on-init coding pattern */ public static void injectAtEnd(String css, boolean immediate) { toInjectAtEnd.push(css); inject(immediate); } /** * Add stylesheet data to the document as though it were declared before all * stylesheets previously created by {@link #inject(String)}. * * @param css the CSS contents of the stylesheet */ public static void injectAtStart(String css) { injectAtStart(css, false); } /** * Add stylesheet data to the document as though it were declared before all * stylesheets previously created by {@link #inject(String)}. * * @param css the CSS contents of the stylesheet * @param immediate if true the DOM will be updated immediately * instead of just before returning to the event loop. Using this * option excessively will decrease performance, especially if used * with an inject-css-on-init coding pattern */ public static void injectAtStart(String css, boolean immediate) { toInjectAtStart.unshift(css); inject(immediate); } /** * Add a stylesheet to the document. *

* The returned StyleElement cannot be implemented consistently across all * browsers. Specifically, applications that need to run on Internet * Explorer should not use this method. Call {@link #inject(String)} * instead. * * @param contents the CSS contents of the stylesheet * @return the StyleElement that contains the newly-injected CSS (unreliable * on Internet Explorer) */ public static StyleElement injectStylesheet(String contents) { toInject.push(contents); return flush(toInject); } /** * Add stylesheet data to the document as though it were declared after all * stylesheets previously created by {@link #injectStylesheet(String)}. *

* The returned StyleElement cannot be implemented consistently across all * browsers. Specifically, applications that need to run on Internet * Explorer should not use this method. Call {@link #injectAtEnd(String)} * instead. * * @param contents the CSS contents of the stylesheet * @return the StyleElement that contains the newly-injected CSS (unreliable * on Internet Explorer) */ public static StyleElement injectStylesheetAtEnd(String contents) { toInjectAtEnd.push(contents); return flush(toInjectAtEnd); } /** * Add stylesheet data to the document as though it were declared before any * stylesheet previously created by {@link #injectStylesheet(String)}. *

* The returned StyleElement cannot be implemented consistently across all * browsers. Specifically, applications that need to run on Internet * Explorer should not use this method. Call {@link #injectAtStart(String, boolean)} * instead. * * @param contents the CSS contents of the stylesheet * @return the StyleElement that contains the newly-injected CSS (unreliable * on Internet Explorer) */ public static StyleElement injectStylesheetAtStart(String contents) { toInjectAtStart.unshift(contents); return flush(toInjectAtStart); } /** * 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. Specifically, applications that * need to run on Internet Explorer should not use this method. * * @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); } /** * The which parameter is used to support the deprecated API. */ private static StyleElement flush(JavaScriptObject which) { StyleElement toReturn = null; StyleElement maybeReturn; if (toInjectAtStart.length() != 0) { String css = toInjectAtStart.join(""); maybeReturn = StyleInjectorImpl.IMPL.injectStyleSheetAtStart(css); if (toInjectAtStart == which) { toReturn = maybeReturn; } toInjectAtStart.setLength(0); } if (toInject.length() != 0) { String css = toInject.join(""); maybeReturn = StyleInjectorImpl.IMPL.injectStyleSheet(css); if (toInject == which) { toReturn = maybeReturn; } toInject.setLength(0); } if (toInjectAtEnd.length() != 0) { String css = toInjectAtEnd.join(""); maybeReturn = StyleInjectorImpl.IMPL.injectStyleSheetAtEnd(css); if (toInjectAtEnd == which) { toReturn = maybeReturn; } toInjectAtEnd.setLength(0); } needsInjection = false; return toReturn; } private static void inject(boolean immediate) { if (immediate) { flush(null); } else { schedule(); } } private static void schedule() { if (!needsInjection) { needsInjection = true; Scheduler.get().scheduleFinally(flusher); } } /** * Utility class. */ private StyleInjector() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy