// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_CUSTOM_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_CUSTOM_ELEMENT_H_

#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/create_element_flags.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/platform/text/character.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/text/code_point_iterator.h"
#include "third_party/blink/renderer/platform/wtf/text/utf16.h"

namespace blink {

class Document;
class Element;
class HTMLElement;
class HTMLFormElement;
class QualifiedName;
class CustomElementDefinition;
class CustomElementReaction;

class CORE_EXPORT CustomElement {
  STATIC_ONLY(CustomElement);

 public:
  static CustomElementDefinition* DefinitionForElement(const Element*);

  static void AddEmbedderCustomElementName(const AtomicString& name);

  static bool IsValidName(const AtomicString& name) {
    return IsValidName(name, true);
  }

  static bool IsValidName(const AtomicString& name,
                          bool including_embedder_names) {
    if (including_embedder_names && EmbedderCustomElementNames().Contains(name))
      return true;

    // This quickly rejects all common built-in element names.
    // name contains a U+002D (-)
    if (name.find('-', 1) == kNotFound)
      return false;

    // name's 0th code point is an ASCII lower alpha
    if (!IsAsciiLower(name[0])) {
      return false;
    }

    // https://github.com/whatwg/html/pull/7991
    // name is a valid element local name
    if (!Document::IsValidElementLocalName(name)) {
      return false;
    }
    // name does not contain any ASCII upper alphas
    if (!name.ContainsNoAsciiUpper()) {
      return false;
    }

    return !IsHyphenatedSpecElementName(name);
  }

  static bool ShouldCreateCustomElement(const AtomicString& local_name);
  static bool ShouldCreateCustomElement(const QualifiedName&);
  static bool ShouldCreateCustomizedBuiltinElement(
      const AtomicString& local_name,
      const Document&);
  static bool ShouldCreateCustomizedBuiltinElement(const QualifiedName&,
                                                   const Document&);

  // Look up a definition, and create an autonomous custom element if
  // it's found.
  static HTMLElement* CreateCustomElement(Document&,
                                          const QualifiedName&,
                                          const CreateElementFlags);

  // Creates "uncustomized" or "undefined" state element. This should be
  // used when CustomElementDefinition is not found.
  static Element* CreateUncustomizedOrUndefinedElement(
      Document&,
      const QualifiedName&,
      const CreateElementFlags,
      const AtomicString& is_value,
      CustomElementRegistryAssignment registry_assignment);
  static HTMLElement* CreateFailedElement(Document&,
                                          const QualifiedName&,
                                          CustomElementRegistry*);

  static void Enqueue(Element&, CustomElementReaction&);
  static void EnqueueConnectedCallback(Element&);
  static void EnqueueConnectedMoveCallback(Element&);
  static void EnqueueDisconnectedCallback(Element&);
  static void EnqueueAdoptedCallback(Element&,
                                     Document& old_owner,
                                     Document& new_owner);
  static void EnqueueAttributeChangedCallback(Element&,
                                              const QualifiedName&,
                                              const AtomicString& old_value,
                                              const AtomicString& new_value);
  static void EnqueueFormAssociatedCallback(Element& element,
                                            HTMLFormElement* nullable_form);
  static void EnqueueFormResetCallback(Element& element);
  static void EnqueueFormDisabledCallback(Element& element, bool is_disabled);
  static void EnqueueFormStateRestoreCallback(Element& element,
                                              const V8ControlValue* value,
                                              const String& mode);
  static void EnqueueToolFillCallback(Element& element, const String& value);

  static void TryToUpgrade(Element&);

  static void AddEmbedderCustomElementNameForTesting(const AtomicString& name,
                                                     ExceptionState&);

 private:
  // Some existing specs have element names with hyphens in them,
  // like font-face in SVG. The custom elements spec explicitly
  // disallows these as custom element names.
  // https://html.spec.whatwg.org/C/#valid-custom-element-name
  static bool IsHyphenatedSpecElementName(const AtomicString&);

  static Vector<AtomicString>& EmbedderCustomElementNames();

  enum CreateUUCheckLevel {
    kCheckAll,
    // QualifiedName is a valid custom element name, and is_value is null.
    kQNameIsValid,
  };
  template <CreateUUCheckLevel>
  static Element* CreateUncustomizedOrUndefinedElementTemplate(
      Document&,
      const QualifiedName&,
      const CreateElementFlags,
      const AtomicString& is_value,
      CustomElementRegistryAssignment registry_assignment);
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_CUSTOM_ELEMENT_H_
