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

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

/*
 * 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.dom.client;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * Works around an IE problem where multiple images trying to load at the same
 * time will generate a request per image. We fix this by only allowing the
 * first image of a given URL to set its source immediately, but simultaneous
 * requests for the same URL don't actually get their source set until the
 * original load is complete.
 */
class ImageSrcIE6 {

  /**
   * A native map of image source URL strings to Image objects. All Image
   * objects with values in this map are waiting on an asynchronous load to
   * complete and have event handlers hooked. The moment the image finishes
   * loading, it will be removed from this map.
   */
  private static JavaScriptObject srcImgMap;

  static {
    executeBackgroundImageCacheCommand();
  }

  /**
   * Returns the src of the image, or the pending src if the image is pending.
   */
  public static native String getImgSrc(Element img) /*-{
    return img.__pendingSrc || img.src;
  }-*/;

  /**
   * Sets the src of the image, queuing up with other requests for the same URL
   * if necessary. If the element is a clone, it may think it is in a pending
   * state but will not be updated properly when the image loads, so we need to
   * add it to the queue.
   */
  public static void setImgSrc(Element img, String src) {
    // We can't early out yet because the img may be a clone that needs to be
    // cleaned up and added to the list of children.
    boolean isSameSource = getImgSrc(img).equals(src);

    // Lazy init the map
    if (srcImgMap == null) {
      srcImgMap = JavaScriptObject.createObject();
    }
    // See if this element is already pending.
    String oldSrc = getPendingSrc(img);
    if (oldSrc != null) {
      // The element is pending; there must be a top node for this src.
      Element top = getTop(srcImgMap, oldSrc);
      if (top == null) {
        // This is a clone, so clean it up.
        cleanupExpandos(img);
      } else if (top.equals(img)) {
        if (isSameSource) {
          // Early out as this is the top element, and therefore not a clone.
          return;
        }

        // It's a pending parent.
        removeTop(srcImgMap, top);
      } else if (removeChild(top, img, isSameSource)) {
        // It's a pending child.
        if (isSameSource) {
          // Early out as this is a known child, and therefore not a clone.
          return;
        }
      } else {
        // The child wasn't found, so this is a clone.
        cleanupExpandos(img);
      }
    }

    // Now load the new src URL.
    Element top = getTop(srcImgMap, src);
    if (top == null) {
      // There is no existing pending parent.
      addTop(srcImgMap, img, src);
    } else {
      // There is an existing pending parent.
      addChild(top, img);
    }
  }

  /**
   * Adds an image as a child to a pending parent.
   */
  private static native void addChild(Element parent, Element child) /*-{
    parent.__kids.push(child);
    child.__pendingSrc = parent.__pendingSrc;
  }-*/;

  /**
   * Sets an image as the pending parent for the specified URL.
   */
  private static native void addTop(JavaScriptObject srcImgMap, Element img,
      String src) /*-{
    // No outstanding requests; load the image.
    img.src = src;

    // If the image was in cache, the load may have just happened synchronously.
    if (img.complete) {
      // We're done
      return;
    }

    // Image is loading asynchronously; put in map for chaining.
    img.__kids = [];
    img.__pendingSrc = src;
    srcImgMap[src] = img;

    var _onload = img.onload, _onerror = img.onerror, _onabort = img.onabort;

    // Same cleanup code matter what state we end up in.
    function finish(_originalHandler) {
      // Grab a copy of the kids.
      var kids = img.__kids;
      img.__cleanup();

      // Set the src for all kids in a timer to ensure caching has happened.
      window.setTimeout(function() {
        for (var i = 0; i < kids.length; ++i) {
          var kid = kids[i];
          if (kid.__pendingSrc == src) {
            kid.src = src;
            kid.__pendingSrc = null;
          }
        }
      }, 0);      

      // Call the original handler, if any.
      _originalHandler && _originalHandler.call(img);
    }

    img.onload = function() {
      finish(_onload);
    }
    img.onerror = function() {
      finish(_onerror);
    }
    img.onabort = function() {
      finish(_onabort);
    }

    img.__cleanup = function() {
      img.onload = _onload;
      img.onerror = _onerror;
      img.onabort = _onabort;
      img.__cleanup = img.__pendingSrc = img.__kids = null;
      delete srcImgMap[src];
    }
  }-*/;

  /**
   * Cleanup an img element that was created using cloneNode.
   * 
   * @param img the img element
   */
  private static native void cleanupExpandos(Element img) /*-{
    img.__cleanup = img.__pendingSrc = img.__kids = null;
  }-*/;

  private static native void executeBackgroundImageCacheCommand() /*-{
    // Fix IE background image refresh bug, present through IE6
    // see http://www.mister-pixel.com/#Content__state=is_that_simple
    // this only works with IE6 SP1+
    try {
      $doc.execCommand("BackgroundImageCache", false, true);
    } catch (e) {
      // ignore error on other browsers
    }
  }-*/;

  /**
   * Returns the pending src URL of an image, or null if the image
   * has no pending src URL.
   */
  private static native String getPendingSrc(Element img) /*-{
    return img.__pendingSrc;
  }-*/;

  /**
   * Returns the pending parent for the specified URL, or null if
   * there is no pending parent for the specified URL.
   */
  private static native Element getTop(JavaScriptObject srcImgMap, String src) /*-{
    return srcImgMap[src];
  }-*/;

  /**
   * Removes a child image from its pending parent. If checkOnly is true, the
   * method will only check that the child is a child of the parent.
   * 
   * @param parent the top element that is loading the source
   * @param child the child to remove
   * @param checkOnly if true, only verify that the child is a child of parent
   * @return true if the child is found, false if not
   */
  private static native boolean removeChild(Element parent, Element child,
      boolean checkOnly) /*-{
    var kids = parent.__kids;
    for (var i = 0, c = kids.length; i < c; ++i) {
      if (kids[i] === child) {
        if (!checkOnly) {
          kids.splice(i, 1);
          child.__pendingSrc = null;
        }
        return true;
      } 
    }

    // If the child isn't in any kids lists, it must be a clone.
    return false;
  }-*/;

  /**
   * Removes a pending parent's pending status, in preparation for changing its
   * URL to something else.
   */
  private static native void removeTop(JavaScriptObject srcImgMap, Element img) /*-{
    var src = img.__pendingSrc;
    var kids = img.__kids;
    img.__cleanup();

    // Restructure the kids, if any.
    if (img = kids[0]) {
      // Try to elect a new top node.
      img.__pendingSrc = null;
      @com.google.gwt.dom.client.ImageSrcIE6::addTop(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/dom/client/Element;Ljava/lang/String;)(srcImgMap, img, src);
      if (img.__pendingSrc) {
        // It became a top node, add the rest as children.
        kids.splice(0, 1);
        img.__kids = kids;
      } else {
        // It loaded immediately; just finish the rest.
        // This is an extremely unlikely case, but could theoretically happen
        // depending on how a browser's network and UI threads are synchronized.
        for (var i = 1, c = kids.length; i < c; ++i) {
          kids[i].src = src;
          kids[i].__pendingSrc = null;
        }
      }
    }
  }-*/;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy