require "set"

module Loofah
  module HTML5 # :nodoc:
    #
    #  HTML safelist lifted from HTML5lib sanitizer code:
    #
    #    http://code.google.com/p/html5lib/
    #
    # <html5_license>
    #
    #   Copyright (c) 2006-2008 The Authors
    #
    #   Contributors:
    #   James Graham - jg307@cam.ac.uk
    #   Anne van Kesteren - annevankesteren@gmail.com
    #   Lachlan Hunt - lachlan.hunt@lachy.id.au
    #   Matt McDonald - kanashii@kanashii.ca
    #   Sam Ruby - rubys@intertwingly.net
    #   Ian Hickson (Google) - ian@hixie.ch
    #   Thomas Broyer - t.broyer@ltgt.net
    #   Jacques Distler - distler@golem.ph.utexas.edu
    #   Henri Sivonen - hsivonen@iki.fi
    #   The Mozilla Foundation (contributions from Henri Sivonen since 2008)
    #
    #   Permission is hereby granted, free of charge, to any person
    #   obtaining a copy of this software and associated documentation
    #   files (the "Software"), to deal in the Software without
    #   restriction, including without limitation the rights to use, copy,
    #   modify, merge, publish, distribute, sublicense, and/or sell copies
    #   of the Software, and to permit persons to whom the Software is
    #   furnished to do so, subject to the following conditions:
    #
    #   The above copyright notice and this permission notice shall be
    #   included in all copies or substantial portions of the Software.
    #
    #   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    #   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    #   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    #   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    #   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    #   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    #   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    #   DEALINGS IN THE SOFTWARE.
    #
    # </html5_license>
    module SafeList
      ACCEPTABLE_ELEMENTS = Set.new([
                                      "a",
                                      "abbr",
                                      "acronym",
                                      "address",
                                      "area",
                                      "article",
                                      "aside",
                                      "audio",
                                      "b",
                                      "bdi",
                                      "bdo",
                                      "big",
                                      "blockquote",
                                      "br",
                                      "button",
                                      "canvas",
                                      "caption",
                                      "center",
                                      "cite",
                                      "code",
                                      "col",
                                      "colgroup",
                                      "command",
                                      "datalist",
                                      "dd",
                                      "del",
                                      "details",
                                      "dfn",
                                      "dir",
                                      "div",
                                      "dl",
                                      "dt",
                                      "em",
                                      "fieldset",
                                      "figcaption",
                                      "figure",
                                      "font",
                                      "footer",
                                      "form",
                                      "h1",
                                      "h2",
                                      "h3",
                                      "h4",
                                      "h5",
                                      "h6",
                                      "header",
                                      "hr",
                                      "i",
                                      "img",
                                      "input",
                                      "ins",
                                      "kbd",
                                      "label",
                                      "legend",
                                      "li",
                                      "main",
                                      "map",
                                      "mark",
                                      "menu",
                                      "meter",
                                      "nav",
                                      "ol",
                                      "optgroup",
                                      "option",
                                      "output",
                                      "p",
                                      "pre",
                                      "q",
                                      "s",
                                      "samp",
                                      "section",
                                      "select",
                                      "small",
                                      "span",
                                      "strike",
                                      "strong",
                                      "sub",
                                      "summary",
                                      "sup",
                                      "table",
                                      "tbody",
                                      "td",
                                      "textarea",
                                      "tfoot",
                                      "th",
                                      "thead",
                                      "time",
                                      "tr",
                                      "tt",
                                      "u",
                                      "ul",
                                      "var",
                                      "video",
                                    ])

      MATHML_ELEMENTS = Set.new([
                                  "annotation",
                                  "annotation-xml",
                                  "maction",
                                  "math",
                                  "merror",
                                  "mfenced",
                                  "mfrac",
                                  "mi",
                                  "mmultiscripts",
                                  "mn",
                                  "mo",
                                  "mover",
                                  "mpadded",
                                  "mphantom",
                                  "mprescripts",
                                  "mroot",
                                  "mrow",
                                  "mspace",
                                  "msqrt",
                                  "mstyle",
                                  "msub",
                                  "msubsup",
                                  "msup",
                                  "mtable",
                                  "mtd",
                                  "mtext",
                                  "mtr",
                                  "munder",
                                  "munderover",
                                  "none",
                                  "semantics",
                                ])

      SVG_ELEMENTS = Set.new([
                               "a",
                               "animate",
                               "animateColor",
                               "animateMotion",
                               "animateTransform",
                               "circle",
                               "clipPath",
                               "defs",
                               "desc",
                               "ellipse",
                               "feGaussianBlur",
                               "filter",
                               "font-face",
                               "font-face-name",
                               "font-face-src",
                               "foreignObject",
                               "g",
                               "glyph",
                               "hkern",
                               "line",
                               "linearGradient",
                               "marker",
                               "mask",
                               "metadata",
                               "missing-glyph",
                               "mpath",
                               "path",
                               "polygon",
                               "polyline",
                               "radialGradient",
                               "rect",
                               "set",
                               "stop",
                               "svg",
                               "switch",
                               "symbol",
                               "text",
                               "textPath",
                               "title",
                               "tspan",
                               "use",
                             ])

      ACCEPTABLE_ATTRIBUTES = Set.new([
                                        "abbr",
                                        "accept",
                                        "accept-charset",
                                        "accesskey",
                                        "action",
                                        "align",
                                        "alt",
                                        "axis",
                                        "border",
                                        "cellpadding",
                                        "cellspacing",
                                        "char",
                                        "charoff",
                                        "charset",
                                        "checked",
                                        "cite",
                                        "class",
                                        "clear",
                                        "color",
                                        "cols",
                                        "colspan",
                                        "compact",
                                        "contenteditable",
                                        "coords",
                                        "datetime",
                                        "dir",
                                        "disabled",
                                        "enctype",
                                        "for",
                                        "frame",
                                        "headers",
                                        "height",
                                        "href",
                                        "hreflang",
                                        "hspace",
                                        "id",
                                        "ismap",
                                        "label",
                                        "lang",
                                        "longdesc",
                                        "loop",
                                        "loopcount",
                                        "loopend",
                                        "loopstart",
                                        "maxlength",
                                        "media",
                                        "method",
                                        "multiple",
                                        "name",
                                        "nohref",
                                        "noshade",
                                        "nowrap",
                                        "poster",
                                        "preload",
                                        "prompt",
                                        "readonly",
                                        "rel",
                                        "rev",
                                        "rows",
                                        "rowspan",
                                        "rules",
                                        "scope",
                                        "selected",
                                        "shape",
                                        "size",
                                        "span",
                                        "src",
                                        "start",
                                        "style",
                                        "summary",
                                        "tabindex",
                                        "target",
                                        "title",
                                        "type",
                                        "usemap",
                                        "valign",
                                        "value",
                                        "vspace",
                                        "width",
                                        "xml:lang",
                                      ])

      MATHML_ATTRIBUTES = Set.new([
                                    "actiontype",
                                    "align",
                                    "close",
                                    "columnalign",
                                    "columnlines",
                                    "columnspacing",
                                    "columnspan",
                                    "depth",
                                    "display",
                                    "displaystyle",
                                    "encoding",
                                    "equalcolumns",
                                    "equalrows",
                                    "fence",
                                    "fontstyle",
                                    "fontweight",
                                    "frame",
                                    "height",
                                    "linethickness",
                                    "lspace",
                                    "mathbackground",
                                    "mathcolor",
                                    "mathvariant",
                                    "maxsize",
                                    "minsize",
                                    "open",
                                    "other",
                                    "rowalign",
                                    "rowlines",
                                    "rowspacing",
                                    "rowspan",
                                    "rspace",
                                    "scriptlevel",
                                    "selection",
                                    "separator",
                                    "separators",
                                    "stretchy",
                                    "width",
                                    "xlink:href",
                                    "xlink:show",
                                    "xlink:type",
                                    "xmlns",
                                    "xmlns:xlink",
                                  ])

      SVG_ATTRIBUTES = Set.new([
                                 "accent-height",
                                 "accumulate",
                                 "additive",
                                 "alphabetic",
                                 "arabic-form",
                                 "ascent",
                                 "attributeName",
                                 "attributeType",
                                 "baseProfile",
                                 "bbox",
                                 "begin",
                                 "calcMode",
                                 "cap-height",
                                 "class",
                                 "clip-path",
                                 "clip-rule",
                                 "color",
                                 "color-interpolation-filters",
                                 "color-rendering",
                                 "content",
                                 "cx",
                                 "cy",
                                 "d",
                                 "descent",
                                 "display",
                                 "dur",
                                 "dx",
                                 "dy",
                                 "end",
                                 "fill",
                                 "fill-opacity",
                                 "fill-rule",
                                 "filterRes",
                                 "filterUnits",
                                 "font-family",
                                 "font-size",
                                 "font-stretch",
                                 "font-style",
                                 "font-variant",
                                 "font-weight",
                                 "fx",
                                 "fy",
                                 "g1",
                                 "g2",
                                 "glyph-name",
                                 "gradientUnits",
                                 "hanging",
                                 "height",
                                 "horiz-adv-x",
                                 "horiz-origin-x",
                                 "id",
                                 "ideographic",
                                 "k",
                                 "keyPoints",
                                 "keySplines",
                                 "keyTimes",
                                 "lang",
                                 "marker-end",
                                 "marker-mid",
                                 "marker-start",
                                 "markerHeight",
                                 "markerUnits",
                                 "markerWidth",
                                 "maskContentUnits",
                                 "maskUnits",
                                 "mathematical",
                                 "max",
                                 "method",
                                 "min",
                                 "name",
                                 "offset",
                                 "opacity",
                                 "orient",
                                 "origin",
                                 "overline-position",
                                 "overline-thickness",
                                 "panose-1",
                                 "path",
                                 "pathLength",
                                 "patternContentUnits",
                                 "patternTransform",
                                 "patternUnits",
                                 "points",
                                 "preserveAspectRatio",
                                 "primitiveUnits",
                                 "r",
                                 "refX",
                                 "refY",
                                 "repeatCount",
                                 "repeatDur",
                                 "requiredExtensions",
                                 "requiredFeatures",
                                 "restart",
                                 "rotate",
                                 "rx",
                                 "ry",
                                 "slope",
                                 "spacing",
                                 "startOffset",
                                 "stdDeviation",
                                 "stemh",
                                 "stemv",
                                 "stop-color",
                                 "stop-opacity",
                                 "strikethrough-position",
                                 "strikethrough-thickness",
                                 "stroke",
                                 "stroke-dasharray",
                                 "stroke-dashoffset",
                                 "stroke-linecap",
                                 "stroke-linejoin",
                                 "stroke-miterlimit",
                                 "stroke-opacity",
                                 "stroke-width",
                                 "systemLanguage",
                                 "target",
                                 "text-anchor",
                                 "transform",
                                 "type",
                                 "u1",
                                 "u2",
                                 "underline-position",
                                 "underline-thickness",
                                 "unicode",
                                 "unicode-range",
                                 "units-per-em",
                                 "version",
                                 "viewBox",
                                 "visibility",
                                 "width",
                                 "widths",
                                 "x",
                                 "x-height",
                                 "x1",
                                 "x2",
                                 "xlink:actuate",
                                 "xlink:arcrole",
                                 "xlink:href",
                                 "xlink:role",
                                 "xlink:show",
                                 "xlink:title",
                                 "xlink:type",
                                 "xml:base",
                                 "xml:lang",
                                 "xml:space",
                                 "xmlns",
                                 "xmlns:xlink",
                                 "y",
                                 "y1",
                                 "y2",
                                 "zoomAndPan",
                               ])

      ATTR_VAL_IS_URI = Set.new([
                                  "action",
                                  "cite",
                                  "href",
                                  "longdesc",
                                  "poster",
                                  "preload",
                                  "src",
                                  "xlink:href",
                                  "xml:base",
                                ])

      SVG_ATTR_VAL_ALLOWS_REF = Set.new([
                                          "clip-path",
                                          "color-profile",
                                          "cursor",
                                          "fill",
                                          "filter",
                                          "marker",
                                          "marker-end",
                                          "marker-mid",
                                          "marker-start",
                                          "mask",
                                          "stroke",
                                        ])

      SVG_ALLOW_LOCAL_HREF = Set.new([
                                       "altGlyph",
                                       "animate",
                                       "animateColor",
                                       "animateMotion",
                                       "animateTransform",
                                       "cursor",
                                       "feImage",
                                       "filter",
                                       "linearGradient",
                                       "pattern",
                                       "radialGradient",
                                       "set",
                                       "textpath",
                                       "tref",
                                       "use",
                                     ])

      ACCEPTABLE_CSS_PROPERTIES = Set.new([
                                            "azimuth",
                                            "background-color",
                                            "border-bottom-color",
                                            "border-collapse",
                                            "border-color",
                                            "border-left-color",
                                            "border-right-color",
                                            "border-top-color",
                                            "clear",
                                            "color",
                                            "cursor",
                                            "direction",
                                            "display",
                                            "elevation",
                                            "float",
                                            "font",
                                            "font-family",
                                            "font-size",
                                            "font-style",
                                            "font-variant",
                                            "font-weight",
                                            "height",
                                            "letter-spacing",
                                            "line-height",
                                            "list-style",
                                            "list-style-type",
                                            "overflow",
                                            "pause",
                                            "pause-after",
                                            "pause-before",
                                            "pitch",
                                            "pitch-range",
                                            "richness",
                                            "speak",
                                            "speak-header",
                                            "speak-numeral",
                                            "speak-punctuation",
                                            "speech-rate",
                                            "stress",
                                            "text-align",
                                            "text-decoration",
                                            "text-indent",
                                            "unicode-bidi",
                                            "vertical-align",
                                            "voice-family",
                                            "volume",
                                            "white-space",
                                            "width",
                                          ])

      ACCEPTABLE_CSS_KEYWORDS = Set.new([
                                          "!important",
                                          "aqua",
                                          "auto",
                                          "black",
                                          "block",
                                          "blue",
                                          "bold",
                                          "both",
                                          "bottom",
                                          "brown",
                                          "center",
                                          "collapse",
                                          "dashed",
                                          "dotted",
                                          "fuchsia",
                                          "gray",
                                          "green",
                                          "italic",
                                          "left",
                                          "lime",
                                          "maroon",
                                          "medium",
                                          "navy",
                                          "none",
                                          "normal",
                                          "nowrap",
                                          "olive",
                                          "pointer",
                                          "purple",
                                          "red",
                                          "right",
                                          "silver",
                                          "solid",
                                          "teal",
                                          "thin",
                                          "thick",
                                          "top",
                                          "transparent",
                                          "underline",
                                          "white",
                                          "yellow",
                                        ])

      # see https://www.quackit.com/css/functions/
      # omit `url` and `image` from that list
      ACCEPTABLE_CSS_FUNCTIONS = Set.new([
                                           "attr",
                                           "blur",
                                           "brightness",
                                           "calc",
                                           "circle",
                                           "contrast",
                                           "counter",
                                           "counters",
                                           "cubic-bezier",
                                           "drop-shadow",
                                           "ellipse",
                                           "grayscale",
                                           "hsl",
                                           "hsla",
                                           "hue-rotate",
                                           "hwb",
                                           "inset",
                                           "invert",
                                           "linear-gradient",
                                           "matrix",
                                           "matrix3d",
                                           "opacity",
                                           "perspective",
                                           "polygon",
                                           "radial-gradient",
                                           "repeating-linear-gradient",
                                           "repeating-radial-gradient",
                                           "rgb",
                                           "rgba",
                                           "rotate",
                                           "rotate3d",
                                           "rotateX",
                                           "rotateY",
                                           "rotateZ",
                                           "saturate",
                                           "sepia",
                                           "scale",
                                           "scale3d",
                                           "scaleX",
                                           "scaleY",
                                           "scaleZ",
                                           "skew",
                                           "skewX",
                                           "skewY",
                                           "symbols",
                                           "translate",
                                           "translate3d",
                                           "translateX",
                                           "translateY",
                                           "translateZ",
                                         ])

      SHORTHAND_CSS_PROPERTIES = Set.new([
                                           "background",
                                           "border",
                                           "margin",
                                           "padding",
                                         ])

      ACCEPTABLE_SVG_PROPERTIES = Set.new([
                                            "fill",
                                            "fill-opacity",
                                            "fill-rule",
                                            "stroke",
                                            "stroke-width",
                                            "stroke-linecap",
                                            "stroke-linejoin",
                                            "stroke-opacity",
                                          ])

      PROTOCOL_SEPARATOR = /:|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i

      ACCEPTABLE_PROTOCOLS = Set.new([
                                       "afs",
                                       "aim",
                                       "callto",
                                       "data",
                                       "ed2k",
                                       "feed",
                                       "ftp",
                                       "gopher",
                                       "http",
                                       "https",
                                       "irc",
                                       "line",
                                       "mailto",
                                       "news",
                                       "nntp",
                                       "rsync",
                                       "rtsp",
                                       "sftp",
                                       "ssh",
                                       "tag",
                                       "tel",
                                       "telnet",
                                       "urn",
                                       "webcal",
                                       "xmpp",
                                     ])

      ACCEPTABLE_URI_DATA_MEDIATYPES = Set.new([
                                                 "image/gif",
                                                 "image/jpeg",
                                                 "image/png",
                                                 "image/svg+xml",
                                                 "text/css",
                                                 "text/plain",
                                               ])

      # subclasses may define their own versions of these constants
      ALLOWED_ELEMENTS = ACCEPTABLE_ELEMENTS + MATHML_ELEMENTS + SVG_ELEMENTS
      ALLOWED_ATTRIBUTES = ACCEPTABLE_ATTRIBUTES + MATHML_ATTRIBUTES + SVG_ATTRIBUTES
      ALLOWED_CSS_PROPERTIES = ACCEPTABLE_CSS_PROPERTIES
      ALLOWED_CSS_KEYWORDS = ACCEPTABLE_CSS_KEYWORDS
      ALLOWED_CSS_FUNCTIONS = ACCEPTABLE_CSS_FUNCTIONS
      ALLOWED_SVG_PROPERTIES = ACCEPTABLE_SVG_PROPERTIES
      ALLOWED_PROTOCOLS = ACCEPTABLE_PROTOCOLS
      ALLOWED_URI_DATA_MEDIATYPES = ACCEPTABLE_URI_DATA_MEDIATYPES

      VOID_ELEMENTS = Set.new([
                                "area",
                                "base",
                                "br",
                                "col",
                                "embed",
                                "hr",
                                "img",
                                "input",
                                "link",
                                "meta",
                                "param",
                              ])

      # additional tags we should consider safe since we have libxml2 fixing up our documents.
      TAGS_SAFE_WITH_LIBXML2 = Set.new([
                                         "body",
                                         "head",
                                         "html",
                                       ])
      ALLOWED_ELEMENTS_WITH_LIBXML2 = ALLOWED_ELEMENTS + TAGS_SAFE_WITH_LIBXML2
    end

    WhiteList = SafeList
    if Object.respond_to?(:deprecate_constant)
      deprecate_constant :WhiteList
    end

    ::Loofah::MetaHelpers.add_downcased_set_members_to_all_set_constants ::Loofah::HTML5::SafeList
  end
end
