/**
   * NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able to control which mutation
   * events are spawned. This version of the removeChild operation allows us to do so. It is not
   * intended for use by application programs.
   */
  Node internalRemoveChild(Node oldChild, boolean replace) throws DOMException {

    CoreDocumentImpl ownerDocument = ownerDocument();
    if (ownerDocument.errorChecking) {
      if (isReadOnly()) {
        throw new DOMException(
            DOMException.NO_MODIFICATION_ALLOWED_ERR,
            DOMMessageFormatter.formatMessage(
                DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
      }
      if (oldChild != null && oldChild.getParentNode() != this) {
        throw new DOMException(
            DOMException.NOT_FOUND_ERR,
            DOMMessageFormatter.formatMessage(
                DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null));
      }
    }

    ChildNode oldInternal = (ChildNode) oldChild;

    // notify document
    ownerDocument.removingNode(this, oldInternal, replace);

    // update cached length if we have any
    if (fNodeListCache != null) {
      if (fNodeListCache.fLength != -1) {
        fNodeListCache.fLength--;
      }
      if (fNodeListCache.fChildIndex != -1) {
        // if the removed node is the cached node
        // move the cache to its (soon former) previous sibling
        if (fNodeListCache.fChild == oldInternal) {
          fNodeListCache.fChildIndex--;
          fNodeListCache.fChild = oldInternal.previousSibling();
        } else {
          // otherwise just invalidate the cache
          fNodeListCache.fChildIndex = -1;
        }
      }
    }

    // Patch linked list around oldChild
    // Note: lastChild == firstChild.previousSibling
    if (oldInternal == firstChild) {
      // removing first child
      oldInternal.isFirstChild(false);
      firstChild = oldInternal.nextSibling;
      if (firstChild != null) {
        firstChild.isFirstChild(true);
        firstChild.previousSibling = oldInternal.previousSibling;
      }
    } else {
      ChildNode prev = oldInternal.previousSibling;
      ChildNode next = oldInternal.nextSibling;
      prev.nextSibling = next;
      if (next == null) {
        // removing last child
        firstChild.previousSibling = prev;
      } else {
        // removing some other child in the middle
        next.previousSibling = prev;
      }
    }

    // Save previous sibling for normalization checking.
    ChildNode oldPreviousSibling = oldInternal.previousSibling();

    // Remove oldInternal's references to tree
    oldInternal.ownerNode = ownerDocument;
    oldInternal.isOwned(false);
    oldInternal.nextSibling = null;
    oldInternal.previousSibling = null;

    changed();

    // notify document
    ownerDocument.removedNode(this, replace);

    checkNormalizationAfterRemove(oldPreviousSibling);

    return oldInternal;
  } // internalRemoveChild(Node,boolean):Node