/**
   * @param containingHtmlElement the name of the HTML element containing el. If the HTML element is
   *     contained inside a template construct then this name may differ from el's immediate parent.
   */
  private void inspectElement(JobEnvelope source, Element el, ElKey containingHtmlElement) {
    ElKey elKey = ElKey.forElement(el);

    // Recurse early so that ihtml:dynamic elements have been parsed before we
    // process the attributes element list.
    for (Node child : Nodes.childrenOf(el)) {
      inspect(source, child, elKey);
    }

    // For each attribute allowed on this element type, ensure that
    // (1) If it is not specified, and its default value is not allowed, then
    //     it is added with a known safe value.
    // (2) Its value is rewritten as appropriate.
    // We don't have to worry about disallowed attributes since those will
    // not be present in scriptsPerNode.  The TemplateSanitizer should have
    // stripped those out.  The TemplateSanitizer should also have stripped out
    // disallowed elements.
    if (!htmlSchema.isElementAllowed(elKey)) {
      return;
    }

    HTML.Element elInfo = htmlSchema.lookupElement(elKey);
    List<HTML.Attribute> attrs = elInfo.getAttributes();
    if (attrs != null) {
      for (HTML.Attribute a : attrs) {
        AttribKey attrKey = a.getKey();
        if (!htmlSchema.isAttributeAllowed(attrKey)) {
          continue;
        }
        Attr attr = null;
        String aUri = attrKey.ns.uri;
        String aName = attrKey.localName;
        Attr unsafe = el.getAttributeNodeNS(aUri, aName);
        if (unsafe != null && a.getValueCriterion().accept(unsafe.getValue())) {
          attr = unsafe;
        } else if ((a.getDefaultValue() != null
                && !a.getValueCriterion().accept(a.getDefaultValue()))
            || !a.isOptional()) {
          attr = el.getOwnerDocument().createAttributeNS(aUri, aName);
          String safeValue;
          if (a.getType() == HTML.Attribute.Type.URI) {
            safeValue = "" + Nodes.getFilePositionFor(el).source().getUri();
          } else {
            safeValue = a.getSafeValue();
          }
          if (safeValue == null) {
            mq.addMessage(
                IhtmlMessageType.MISSING_ATTRIB, Nodes.getFilePositionFor(el), elKey, attrKey);
            continue;
          }
          attr.setNodeValue(safeValue);
          el.setAttributeNodeNS(attr);
        }
        if (attr != null) {
          inspectHtmlAttribute(source, attr, a);
        }
      }
    }
    scriptsPerNode.put(el, null);
  }
 /**
  * Examines the HTML document and writes messages about problematic portions to the message queue
  * passed to the constructor.
  */
 private void inspect() {
   if (!mq.hasMessageAtLevel(MessageLevel.FATAL_ERROR)) {
     for (IhtmlRoot ihtmlRoot : ihtmlRoots) {
       HtmlEmbeddedContentFinder finder =
           new HtmlEmbeddedContentFinder(htmlSchema, ihtmlRoot.baseUri, mq, mc);
       for (EmbeddedContent c : finder.findEmbeddedContent(ihtmlRoot.root)) {
         Node src = c.getSource();
         if (src instanceof Attr) {
           embeddedContent.put((Attr) src, c);
         }
       }
       inspect(ihtmlRoot.source, ihtmlRoot.root, ElKey.forHtmlElement("div"));
     }
   }
 }
 static AttribKey attrKey(String elQName, String aQName) {
   return AttribKey.forAttribute(
       Namespaces.HTML_DEFAULT, ElKey.forElement(Namespaces.HTML_DEFAULT, elQName), aQName);
 }
 static ElKey elKey(String qname) {
   return ElKey.forElement(Namespaces.HTML_DEFAULT, qname);
 }