fune/dom/base/nsTreeSanitizer.cpp

2643 lines
93 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsTreeSanitizer.h"
#include "mozilla/Algorithm.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/HTMLUnknownElement.h"
#include "mozilla/dom/Link.h"
#include "mozilla/dom/SanitizerBinding.h"
#include "mozilla/dom/ShadowIncludingTreeIterator.h"
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/NullPrincipal.h"
#include "nsAtom.h"
#include "nsCSSPropertyID.h"
#include "nsHashtablesFwd.h"
#include "nsString.h"
#include "nsTHashtable.h"
#include "nsUnicharInputStream.h"
#include "nsAttrName.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsNameSpaceManager.h"
#include "nsNetUtil.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsIParserUtils.h"
#include "mozilla/dom/Document.h"
#include "nsQueryObject.h"
#include <iterator>
using namespace mozilla;
using namespace mozilla::dom;
//
// Thanks to Mark Pilgrim and Sam Ruby for the initial whitelist
//
const nsStaticAtom* const kElementsHTML[] = {
// clang-format off
nsGkAtoms::a,
nsGkAtoms::abbr,
nsGkAtoms::acronym,
nsGkAtoms::address,
nsGkAtoms::area,
nsGkAtoms::article,
nsGkAtoms::aside,
nsGkAtoms::audio,
nsGkAtoms::b,
nsGkAtoms::bdi,
nsGkAtoms::bdo,
nsGkAtoms::big,
nsGkAtoms::blockquote,
// body checked specially
nsGkAtoms::br,
nsGkAtoms::button,
nsGkAtoms::canvas,
nsGkAtoms::caption,
nsGkAtoms::center,
nsGkAtoms::cite,
nsGkAtoms::code,
nsGkAtoms::col,
nsGkAtoms::colgroup,
nsGkAtoms::data,
nsGkAtoms::datalist,
nsGkAtoms::dd,
nsGkAtoms::del,
nsGkAtoms::details,
nsGkAtoms::dfn,
nsGkAtoms::dialog,
nsGkAtoms::dir,
nsGkAtoms::div,
nsGkAtoms::dl,
nsGkAtoms::dt,
nsGkAtoms::em,
nsGkAtoms::fieldset,
nsGkAtoms::figcaption,
nsGkAtoms::figure,
nsGkAtoms::font,
nsGkAtoms::footer,
nsGkAtoms::form,
nsGkAtoms::h1,
nsGkAtoms::h2,
nsGkAtoms::h3,
nsGkAtoms::h4,
nsGkAtoms::h5,
nsGkAtoms::h6,
// head checked specially
nsGkAtoms::header,
nsGkAtoms::hgroup,
nsGkAtoms::hr,
// html checked specially
nsGkAtoms::i,
nsGkAtoms::img,
nsGkAtoms::input,
nsGkAtoms::ins,
nsGkAtoms::kbd,
nsGkAtoms::keygen,
nsGkAtoms::label,
nsGkAtoms::legend,
nsGkAtoms::li,
nsGkAtoms::link,
nsGkAtoms::listing,
nsGkAtoms::main,
nsGkAtoms::map,
nsGkAtoms::mark,
nsGkAtoms::menu,
nsGkAtoms::meta,
nsGkAtoms::meter,
nsGkAtoms::nav,
nsGkAtoms::nobr,
nsGkAtoms::noscript,
nsGkAtoms::ol,
nsGkAtoms::optgroup,
nsGkAtoms::option,
nsGkAtoms::output,
nsGkAtoms::p,
nsGkAtoms::picture,
nsGkAtoms::pre,
nsGkAtoms::progress,
nsGkAtoms::q,
nsGkAtoms::rb,
nsGkAtoms::rp,
nsGkAtoms::rt,
nsGkAtoms::rtc,
nsGkAtoms::ruby,
nsGkAtoms::s,
nsGkAtoms::samp,
nsGkAtoms::section,
nsGkAtoms::select,
nsGkAtoms::small,
nsGkAtoms::source,
nsGkAtoms::span,
nsGkAtoms::strike,
nsGkAtoms::strong,
nsGkAtoms::sub,
nsGkAtoms::summary,
nsGkAtoms::sup,
// style checked specially
nsGkAtoms::table,
nsGkAtoms::tbody,
nsGkAtoms::td,
// template checked and traversed specially
nsGkAtoms::textarea,
nsGkAtoms::tfoot,
nsGkAtoms::th,
nsGkAtoms::thead,
nsGkAtoms::time,
// title checked specially
nsGkAtoms::tr,
nsGkAtoms::track,
nsGkAtoms::tt,
nsGkAtoms::u,
nsGkAtoms::ul,
nsGkAtoms::var,
nsGkAtoms::video,
nsGkAtoms::wbr,
nullptr
// clang-format on
};
const nsStaticAtom* const kAttributesHTML[] = {
// clang-format off
nsGkAtoms::abbr,
nsGkAtoms::accept,
nsGkAtoms::acceptcharset,
nsGkAtoms::accesskey,
nsGkAtoms::action,
nsGkAtoms::alt,
nsGkAtoms::as,
nsGkAtoms::autocomplete,
nsGkAtoms::autofocus,
nsGkAtoms::autoplay,
nsGkAtoms::axis,
nsGkAtoms::_char,
nsGkAtoms::charoff,
nsGkAtoms::charset,
nsGkAtoms::checked,
nsGkAtoms::cite,
nsGkAtoms::_class,
nsGkAtoms::cols,
nsGkAtoms::colspan,
nsGkAtoms::content,
nsGkAtoms::contenteditable,
nsGkAtoms::contextmenu,
nsGkAtoms::controls,
nsGkAtoms::coords,
nsGkAtoms::crossorigin,
nsGkAtoms::datetime,
nsGkAtoms::dir,
nsGkAtoms::disabled,
nsGkAtoms::draggable,
nsGkAtoms::enctype,
nsGkAtoms::face,
nsGkAtoms::_for,
nsGkAtoms::frame,
nsGkAtoms::headers,
nsGkAtoms::height,
nsGkAtoms::hidden,
nsGkAtoms::high,
nsGkAtoms::href,
nsGkAtoms::hreflang,
nsGkAtoms::icon,
nsGkAtoms::id,
nsGkAtoms::integrity,
nsGkAtoms::ismap,
nsGkAtoms::itemid,
nsGkAtoms::itemprop,
nsGkAtoms::itemref,
nsGkAtoms::itemscope,
nsGkAtoms::itemtype,
nsGkAtoms::kind,
nsGkAtoms::label,
nsGkAtoms::lang,
nsGkAtoms::list_,
nsGkAtoms::longdesc,
nsGkAtoms::loop,
nsGkAtoms::low,
nsGkAtoms::max,
nsGkAtoms::maxlength,
nsGkAtoms::media,
nsGkAtoms::method,
nsGkAtoms::min,
nsGkAtoms::minlength,
nsGkAtoms::multiple,
nsGkAtoms::muted,
nsGkAtoms::name,
nsGkAtoms::nohref,
nsGkAtoms::novalidate,
nsGkAtoms::nowrap,
nsGkAtoms::open,
nsGkAtoms::optimum,
nsGkAtoms::pattern,
nsGkAtoms::placeholder,
nsGkAtoms::playbackrate,
nsGkAtoms::poster,
nsGkAtoms::preload,
nsGkAtoms::prompt,
nsGkAtoms::pubdate,
nsGkAtoms::radiogroup,
nsGkAtoms::readonly,
nsGkAtoms::rel,
nsGkAtoms::required,
nsGkAtoms::rev,
nsGkAtoms::reversed,
nsGkAtoms::role,
nsGkAtoms::rows,
nsGkAtoms::rowspan,
nsGkAtoms::rules,
nsGkAtoms::scoped,
nsGkAtoms::scope,
nsGkAtoms::selected,
nsGkAtoms::shape,
nsGkAtoms::span,
nsGkAtoms::spellcheck,
nsGkAtoms::src,
nsGkAtoms::srclang,
nsGkAtoms::start,
nsGkAtoms::summary,
nsGkAtoms::tabindex,
nsGkAtoms::target,
nsGkAtoms::title,
nsGkAtoms::type,
nsGkAtoms::usemap,
nsGkAtoms::value,
nsGkAtoms::width,
nsGkAtoms::wrap,
nullptr
// clang-format on
};
const nsStaticAtom* const kPresAttributesHTML[] = {
// clang-format off
nsGkAtoms::align,
nsGkAtoms::background,
nsGkAtoms::bgcolor,
nsGkAtoms::border,
nsGkAtoms::cellpadding,
nsGkAtoms::cellspacing,
nsGkAtoms::color,
nsGkAtoms::compact,
nsGkAtoms::clear,
nsGkAtoms::hspace,
nsGkAtoms::noshade,
nsGkAtoms::pointSize,
nsGkAtoms::size,
nsGkAtoms::valign,
nsGkAtoms::vspace,
nullptr
// clang-format on
};
// List of HTML attributes with URLs that the
// browser will fetch. Should be kept in sync with
// https://html.spec.whatwg.org/multipage/indices.html#attributes-3
const nsStaticAtom* const kURLAttributesHTML[] = {
// clang-format off
nsGkAtoms::action,
nsGkAtoms::href,
nsGkAtoms::src,
nsGkAtoms::longdesc,
nsGkAtoms::cite,
nsGkAtoms::background,
nsGkAtoms::formaction,
nsGkAtoms::data,
nsGkAtoms::ping,
nsGkAtoms::poster,
nullptr
// clang-format on
};
const nsStaticAtom* const kElementsSVG[] = {
nsGkAtoms::a, // a
nsGkAtoms::circle, // circle
nsGkAtoms::clipPath, // clipPath
nsGkAtoms::colorProfile, // color-profile
nsGkAtoms::cursor, // cursor
nsGkAtoms::defs, // defs
nsGkAtoms::desc, // desc
nsGkAtoms::ellipse, // ellipse
nsGkAtoms::elevation, // elevation
nsGkAtoms::erode, // erode
nsGkAtoms::ex, // ex
nsGkAtoms::exact, // exact
nsGkAtoms::exponent, // exponent
nsGkAtoms::feBlend, // feBlend
nsGkAtoms::feColorMatrix, // feColorMatrix
nsGkAtoms::feComponentTransfer, // feComponentTransfer
nsGkAtoms::feComposite, // feComposite
nsGkAtoms::feConvolveMatrix, // feConvolveMatrix
nsGkAtoms::feDiffuseLighting, // feDiffuseLighting
nsGkAtoms::feDisplacementMap, // feDisplacementMap
nsGkAtoms::feDistantLight, // feDistantLight
nsGkAtoms::feDropShadow, // feDropShadow
nsGkAtoms::feFlood, // feFlood
nsGkAtoms::feFuncA, // feFuncA
nsGkAtoms::feFuncB, // feFuncB
nsGkAtoms::feFuncG, // feFuncG
nsGkAtoms::feFuncR, // feFuncR
nsGkAtoms::feGaussianBlur, // feGaussianBlur
nsGkAtoms::feImage, // feImage
nsGkAtoms::feMerge, // feMerge
nsGkAtoms::feMergeNode, // feMergeNode
nsGkAtoms::feMorphology, // feMorphology
nsGkAtoms::feOffset, // feOffset
nsGkAtoms::fePointLight, // fePointLight
nsGkAtoms::feSpecularLighting, // feSpecularLighting
nsGkAtoms::feSpotLight, // feSpotLight
nsGkAtoms::feTile, // feTile
nsGkAtoms::feTurbulence, // feTurbulence
nsGkAtoms::filter, // filter
nsGkAtoms::font, // font
nsGkAtoms::font_face, // font-face
nsGkAtoms::font_face_format, // font-face-format
nsGkAtoms::font_face_name, // font-face-name
nsGkAtoms::font_face_src, // font-face-src
nsGkAtoms::font_face_uri, // font-face-uri
nsGkAtoms::foreignObject, // foreignObject
nsGkAtoms::g, // g
// glyph
nsGkAtoms::glyphRef, // glyphRef
// hkern
nsGkAtoms::image, // image
nsGkAtoms::line, // line
nsGkAtoms::linearGradient, // linearGradient
nsGkAtoms::marker, // marker
nsGkAtoms::mask, // mask
nsGkAtoms::metadata, // metadata
nsGkAtoms::missingGlyph, // missingGlyph
nsGkAtoms::mpath, // mpath
nsGkAtoms::path, // path
nsGkAtoms::pattern, // pattern
nsGkAtoms::polygon, // polygon
nsGkAtoms::polyline, // polyline
nsGkAtoms::radialGradient, // radialGradient
nsGkAtoms::rect, // rect
nsGkAtoms::stop, // stop
nsGkAtoms::svg, // svg
nsGkAtoms::svgSwitch, // switch
nsGkAtoms::symbol, // symbol
nsGkAtoms::text, // text
nsGkAtoms::textPath, // textPath
nsGkAtoms::title, // title
nsGkAtoms::tref, // tref
nsGkAtoms::tspan, // tspan
nsGkAtoms::use, // use
nsGkAtoms::view, // view
// vkern
nullptr};
constexpr const nsStaticAtom* const kAttributesSVG[] = {
// accent-height
nsGkAtoms::accumulate, // accumulate
nsGkAtoms::additive, // additive
nsGkAtoms::alignment_baseline, // alignment-baseline
// alphabetic
nsGkAtoms::amplitude, // amplitude
// arabic-form
// ascent
nsGkAtoms::attributeName, // attributeName
nsGkAtoms::attributeType, // attributeType
nsGkAtoms::azimuth, // azimuth
nsGkAtoms::baseFrequency, // baseFrequency
nsGkAtoms::baseline_shift, // baseline-shift
// baseProfile
// bbox
nsGkAtoms::begin, // begin
nsGkAtoms::bias, // bias
nsGkAtoms::by, // by
nsGkAtoms::calcMode, // calcMode
// cap-height
nsGkAtoms::_class, // class
nsGkAtoms::clip_path, // clip-path
nsGkAtoms::clip_rule, // clip-rule
nsGkAtoms::clipPathUnits, // clipPathUnits
nsGkAtoms::color, // color
nsGkAtoms::colorInterpolation, // color-interpolation
nsGkAtoms::colorInterpolationFilters, // color-interpolation-filters
nsGkAtoms::cursor, // cursor
nsGkAtoms::cx, // cx
nsGkAtoms::cy, // cy
nsGkAtoms::d, // d
// descent
nsGkAtoms::diffuseConstant, // diffuseConstant
nsGkAtoms::direction, // direction
nsGkAtoms::display, // display
nsGkAtoms::divisor, // divisor
nsGkAtoms::dominant_baseline, // dominant-baseline
nsGkAtoms::dur, // dur
nsGkAtoms::dx, // dx
nsGkAtoms::dy, // dy
nsGkAtoms::edgeMode, // edgeMode
nsGkAtoms::elevation, // elevation
// enable-background
nsGkAtoms::end, // end
nsGkAtoms::fill, // fill
nsGkAtoms::fill_opacity, // fill-opacity
nsGkAtoms::fill_rule, // fill-rule
nsGkAtoms::filter, // filter
nsGkAtoms::filterUnits, // filterUnits
nsGkAtoms::flood_color, // flood-color
nsGkAtoms::flood_opacity, // flood-opacity
// XXX focusable
nsGkAtoms::font, // font
nsGkAtoms::font_family, // font-family
nsGkAtoms::font_size, // font-size
nsGkAtoms::font_size_adjust, // font-size-adjust
nsGkAtoms::font_stretch, // font-stretch
nsGkAtoms::font_style, // font-style
nsGkAtoms::font_variant, // font-variant
nsGkAtoms::fontWeight, // font-weight
nsGkAtoms::format, // format
nsGkAtoms::from, // from
nsGkAtoms::fx, // fx
nsGkAtoms::fy, // fy
// g1
// g2
// glyph-name
// glyphRef
// glyph-orientation-horizontal
// glyph-orientation-vertical
nsGkAtoms::gradientTransform, // gradientTransform
nsGkAtoms::gradientUnits, // gradientUnits
nsGkAtoms::height, // height
nsGkAtoms::href,
// horiz-adv-x
// horiz-origin-x
// horiz-origin-y
nsGkAtoms::id, // id
// ideographic
nsGkAtoms::image_rendering, // image-rendering
nsGkAtoms::in, // in
nsGkAtoms::in2, // in2
nsGkAtoms::intercept, // intercept
// k
nsGkAtoms::k1, // k1
nsGkAtoms::k2, // k2
nsGkAtoms::k3, // k3
nsGkAtoms::k4, // k4
// kerning
nsGkAtoms::kernelMatrix, // kernelMatrix
nsGkAtoms::kernelUnitLength, // kernelUnitLength
nsGkAtoms::keyPoints, // keyPoints
nsGkAtoms::keySplines, // keySplines
nsGkAtoms::keyTimes, // keyTimes
nsGkAtoms::lang, // lang
// lengthAdjust
nsGkAtoms::letter_spacing, // letter-spacing
nsGkAtoms::lighting_color, // lighting-color
nsGkAtoms::limitingConeAngle, // limitingConeAngle
// local
nsGkAtoms::marker, // marker
nsGkAtoms::marker_end, // marker-end
nsGkAtoms::marker_mid, // marker-mid
nsGkAtoms::marker_start, // marker-start
nsGkAtoms::markerHeight, // markerHeight
nsGkAtoms::markerUnits, // markerUnits
nsGkAtoms::markerWidth, // markerWidth
nsGkAtoms::mask, // mask
nsGkAtoms::maskContentUnits, // maskContentUnits
nsGkAtoms::maskUnits, // maskUnits
// mathematical
nsGkAtoms::max, // max
nsGkAtoms::media, // media
nsGkAtoms::method, // method
nsGkAtoms::min, // min
nsGkAtoms::mode, // mode
nsGkAtoms::name, // name
nsGkAtoms::numOctaves, // numOctaves
nsGkAtoms::offset, // offset
nsGkAtoms::opacity, // opacity
nsGkAtoms::_operator, // operator
nsGkAtoms::order, // order
nsGkAtoms::orient, // orient
nsGkAtoms::orientation, // orientation
// origin
// overline-position
// overline-thickness
nsGkAtoms::overflow, // overflow
// panose-1
nsGkAtoms::path, // path
nsGkAtoms::pathLength, // pathLength
nsGkAtoms::patternContentUnits, // patternContentUnits
nsGkAtoms::patternTransform, // patternTransform
nsGkAtoms::patternUnits, // patternUnits
nsGkAtoms::pointer_events, // pointer-events XXX is this safe?
nsGkAtoms::points, // points
nsGkAtoms::pointsAtX, // pointsAtX
nsGkAtoms::pointsAtY, // pointsAtY
nsGkAtoms::pointsAtZ, // pointsAtZ
nsGkAtoms::preserveAlpha, // preserveAlpha
nsGkAtoms::preserveAspectRatio, // preserveAspectRatio
nsGkAtoms::primitiveUnits, // primitiveUnits
nsGkAtoms::r, // r
nsGkAtoms::radius, // radius
nsGkAtoms::refX, // refX
nsGkAtoms::refY, // refY
nsGkAtoms::repeatCount, // repeatCount
nsGkAtoms::repeatDur, // repeatDur
nsGkAtoms::requiredExtensions, // requiredExtensions
nsGkAtoms::requiredFeatures, // requiredFeatures
nsGkAtoms::restart, // restart
nsGkAtoms::result, // result
nsGkAtoms::rotate, // rotate
nsGkAtoms::rx, // rx
nsGkAtoms::ry, // ry
nsGkAtoms::scale, // scale
nsGkAtoms::seed, // seed
nsGkAtoms::shape_rendering, // shape-rendering
nsGkAtoms::slope, // slope
nsGkAtoms::spacing, // spacing
nsGkAtoms::specularConstant, // specularConstant
nsGkAtoms::specularExponent, // specularExponent
nsGkAtoms::spreadMethod, // spreadMethod
nsGkAtoms::startOffset, // startOffset
nsGkAtoms::stdDeviation, // stdDeviation
// stemh
// stemv
nsGkAtoms::stitchTiles, // stitchTiles
nsGkAtoms::stop_color, // stop-color
nsGkAtoms::stop_opacity, // stop-opacity
// strikethrough-position
// strikethrough-thickness
nsGkAtoms::string, // string
nsGkAtoms::stroke, // stroke
nsGkAtoms::stroke_dasharray, // stroke-dasharray
nsGkAtoms::stroke_dashoffset, // stroke-dashoffset
nsGkAtoms::stroke_linecap, // stroke-linecap
nsGkAtoms::stroke_linejoin, // stroke-linejoin
nsGkAtoms::stroke_miterlimit, // stroke-miterlimit
nsGkAtoms::stroke_opacity, // stroke-opacity
nsGkAtoms::stroke_width, // stroke-width
nsGkAtoms::surfaceScale, // surfaceScale
nsGkAtoms::systemLanguage, // systemLanguage
nsGkAtoms::tableValues, // tableValues
nsGkAtoms::target, // target
nsGkAtoms::targetX, // targetX
nsGkAtoms::targetY, // targetY
nsGkAtoms::text_anchor, // text-anchor
nsGkAtoms::text_decoration, // text-decoration
// textLength
nsGkAtoms::text_rendering, // text-rendering
nsGkAtoms::title, // title
nsGkAtoms::to, // to
nsGkAtoms::transform, // transform
nsGkAtoms::transform_origin, // transform-origin
nsGkAtoms::type, // type
// u1
// u2
// underline-position
// underline-thickness
// unicode
nsGkAtoms::unicode_bidi, // unicode-bidi
// unicode-range
// units-per-em
// v-alphabetic
// v-hanging
// v-ideographic
// v-mathematical
nsGkAtoms::values, // values
nsGkAtoms::vector_effect, // vector-effect
// vert-adv-y
// vert-origin-x
// vert-origin-y
nsGkAtoms::viewBox, // viewBox
nsGkAtoms::viewTarget, // viewTarget
nsGkAtoms::visibility, // visibility
nsGkAtoms::width, // width
// widths
nsGkAtoms::word_spacing, // word-spacing
nsGkAtoms::writing_mode, // writing-mode
nsGkAtoms::x, // x
// x-height
nsGkAtoms::x1, // x1
nsGkAtoms::x2, // x2
nsGkAtoms::xChannelSelector, // xChannelSelector
nsGkAtoms::y, // y
nsGkAtoms::y1, // y1
nsGkAtoms::y2, // y2
nsGkAtoms::yChannelSelector, // yChannelSelector
nsGkAtoms::z, // z
nsGkAtoms::zoomAndPan, // zoomAndPan
nullptr};
constexpr const nsStaticAtom* const kURLAttributesSVG[] = {nsGkAtoms::href,
nullptr};
static_assert(AllOf(std::begin(kURLAttributesSVG), std::end(kURLAttributesSVG),
[](auto aURLAttributeSVG) {
return AnyOf(std::begin(kAttributesSVG),
std::end(kAttributesSVG),
[&](auto aAttributeSVG) {
return aAttributeSVG == aURLAttributeSVG;
});
}));
const nsStaticAtom* const kElementsMathML[] = {
nsGkAtoms::abs_, // abs
nsGkAtoms::_and, // and
nsGkAtoms::annotation_, // annotation
nsGkAtoms::annotation_xml_, // annotation-xml
nsGkAtoms::apply_, // apply
nsGkAtoms::approx_, // approx
nsGkAtoms::arccos_, // arccos
nsGkAtoms::arccosh_, // arccosh
nsGkAtoms::arccot_, // arccot
nsGkAtoms::arccoth_, // arccoth
nsGkAtoms::arccsc_, // arccsc
nsGkAtoms::arccsch_, // arccsch
nsGkAtoms::arcsec_, // arcsec
nsGkAtoms::arcsech_, // arcsech
nsGkAtoms::arcsin_, // arcsin
nsGkAtoms::arcsinh_, // arcsinh
nsGkAtoms::arctan_, // arctan
nsGkAtoms::arctanh_, // arctanh
nsGkAtoms::arg_, // arg
nsGkAtoms::bind_, // bind
nsGkAtoms::bvar_, // bvar
nsGkAtoms::card_, // card
nsGkAtoms::cartesianproduct_, // cartesianproduct
nsGkAtoms::cbytes_, // cbytes
nsGkAtoms::ceiling, // ceiling
nsGkAtoms::cerror_, // cerror
nsGkAtoms::ci_, // ci
nsGkAtoms::cn_, // cn
nsGkAtoms::codomain_, // codomain
nsGkAtoms::complexes_, // complexes
nsGkAtoms::compose_, // compose
nsGkAtoms::condition_, // condition
nsGkAtoms::conjugate_, // conjugate
nsGkAtoms::cos_, // cos
nsGkAtoms::cosh_, // cosh
nsGkAtoms::cot_, // cot
nsGkAtoms::coth_, // coth
nsGkAtoms::cs_, // cs
nsGkAtoms::csc_, // csc
nsGkAtoms::csch_, // csch
nsGkAtoms::csymbol_, // csymbol
nsGkAtoms::curl_, // curl
nsGkAtoms::declare, // declare
nsGkAtoms::degree_, // degree
nsGkAtoms::determinant_, // determinant
nsGkAtoms::diff_, // diff
nsGkAtoms::divergence_, // divergence
nsGkAtoms::divide_, // divide
nsGkAtoms::domain_, // domain
nsGkAtoms::domainofapplication_, // domainofapplication
nsGkAtoms::el, // el
nsGkAtoms::emptyset_, // emptyset
nsGkAtoms::eq_, // eq
nsGkAtoms::equivalent_, // equivalent
nsGkAtoms::eulergamma_, // eulergamma
nsGkAtoms::exists_, // exists
nsGkAtoms::exp_, // exp
nsGkAtoms::exponentiale_, // exponentiale
nsGkAtoms::factorial_, // factorial
nsGkAtoms::factorof_, // factorof
nsGkAtoms::_false, // false
nsGkAtoms::floor, // floor
nsGkAtoms::fn_, // fn
nsGkAtoms::forall_, // forall
nsGkAtoms::gcd_, // gcd
nsGkAtoms::geq_, // geq
nsGkAtoms::grad, // grad
nsGkAtoms::gt_, // gt
nsGkAtoms::ident_, // ident
nsGkAtoms::image, // image
nsGkAtoms::imaginary_, // imaginary
nsGkAtoms::imaginaryi_, // imaginaryi
nsGkAtoms::implies_, // implies
nsGkAtoms::in, // in
nsGkAtoms::infinity, // infinity
nsGkAtoms::int_, // int
nsGkAtoms::integers_, // integers
nsGkAtoms::intersect_, // intersect
nsGkAtoms::interval_, // interval
nsGkAtoms::inverse_, // inverse
nsGkAtoms::lambda_, // lambda
nsGkAtoms::laplacian_, // laplacian
nsGkAtoms::lcm_, // lcm
nsGkAtoms::leq_, // leq
nsGkAtoms::limit_, // limit
nsGkAtoms::list_, // list
nsGkAtoms::ln_, // ln
nsGkAtoms::log_, // log
nsGkAtoms::logbase_, // logbase
nsGkAtoms::lowlimit_, // lowlimit
nsGkAtoms::lt_, // lt
nsGkAtoms::maction_, // maction
nsGkAtoms::maligngroup_, // maligngroup
nsGkAtoms::malignmark_, // malignmark
nsGkAtoms::math, // math
nsGkAtoms::matrix, // matrix
nsGkAtoms::matrixrow_, // matrixrow
nsGkAtoms::max, // max
nsGkAtoms::mean_, // mean
nsGkAtoms::median_, // median
nsGkAtoms::menclose_, // menclose
nsGkAtoms::merror_, // merror
nsGkAtoms::mfrac_, // mfrac
nsGkAtoms::mglyph_, // mglyph
nsGkAtoms::mi_, // mi
nsGkAtoms::min, // min
nsGkAtoms::minus_, // minus
nsGkAtoms::mlabeledtr_, // mlabeledtr
nsGkAtoms::mlongdiv_, // mlongdiv
nsGkAtoms::mmultiscripts_, // mmultiscripts
nsGkAtoms::mn_, // mn
nsGkAtoms::mo_, // mo
nsGkAtoms::mode, // mode
nsGkAtoms::moment_, // moment
nsGkAtoms::momentabout_, // momentabout
nsGkAtoms::mover_, // mover
nsGkAtoms::mpadded_, // mpadded
nsGkAtoms::mphantom_, // mphantom
nsGkAtoms::mprescripts_, // mprescripts
nsGkAtoms::mroot_, // mroot
nsGkAtoms::mrow_, // mrow
nsGkAtoms::ms_, // ms
nsGkAtoms::mscarries_, // mscarries
nsGkAtoms::mscarry_, // mscarry
nsGkAtoms::msgroup_, // msgroup
nsGkAtoms::msline_, // msline
nsGkAtoms::mspace_, // mspace
nsGkAtoms::msqrt_, // msqrt
nsGkAtoms::msrow_, // msrow
nsGkAtoms::mstack_, // mstack
nsGkAtoms::mstyle_, // mstyle
nsGkAtoms::msub_, // msub
nsGkAtoms::msubsup_, // msubsup
nsGkAtoms::msup_, // msup
nsGkAtoms::mtable_, // mtable
nsGkAtoms::mtd_, // mtd
nsGkAtoms::mtext_, // mtext
nsGkAtoms::mtr_, // mtr
nsGkAtoms::munder_, // munder
nsGkAtoms::munderover_, // munderover
nsGkAtoms::naturalnumbers_, // naturalnumbers
nsGkAtoms::neq_, // neq
nsGkAtoms::none, // none
nsGkAtoms::_not, // not
nsGkAtoms::notanumber_, // notanumber
nsGkAtoms::note_, // note
nsGkAtoms::notin_, // notin
nsGkAtoms::notprsubset_, // notprsubset
nsGkAtoms::notsubset_, // notsubset
nsGkAtoms::_or, // or
nsGkAtoms::otherwise, // otherwise
nsGkAtoms::outerproduct_, // outerproduct
nsGkAtoms::partialdiff_, // partialdiff
nsGkAtoms::pi_, // pi
nsGkAtoms::piece_, // piece
nsGkAtoms::piecewise_, // piecewise
nsGkAtoms::plus_, // plus
nsGkAtoms::power_, // power
nsGkAtoms::primes_, // primes
nsGkAtoms::product_, // product
nsGkAtoms::prsubset_, // prsubset
nsGkAtoms::quotient_, // quotient
nsGkAtoms::rationals_, // rationals
nsGkAtoms::real_, // real
nsGkAtoms::reals_, // reals
nsGkAtoms::reln_, // reln
nsGkAtoms::rem, // rem
nsGkAtoms::root_, // root
nsGkAtoms::scalarproduct_, // scalarproduct
nsGkAtoms::sdev_, // sdev
nsGkAtoms::sec_, // sec
nsGkAtoms::sech_, // sech
nsGkAtoms::selector_, // selector
nsGkAtoms::semantics_, // semantics
nsGkAtoms::sep_, // sep
nsGkAtoms::set, // set
nsGkAtoms::setdiff_, // setdiff
nsGkAtoms::share_, // share
nsGkAtoms::sin_, // sin
nsGkAtoms::sinh_, // sinh
nsGkAtoms::subset_, // subset
nsGkAtoms::sum, // sum
nsGkAtoms::tan_, // tan
nsGkAtoms::tanh_, // tanh
nsGkAtoms::tendsto_, // tendsto
nsGkAtoms::times_, // times
nsGkAtoms::transpose_, // transpose
nsGkAtoms::_true, // true
nsGkAtoms::union_, // union
nsGkAtoms::uplimit_, // uplimit
nsGkAtoms::variance_, // variance
nsGkAtoms::vector_, // vector
nsGkAtoms::vectorproduct_, // vectorproduct
nsGkAtoms::xor_, // xor
nullptr};
const nsStaticAtom* const kAttributesMathML[] = {
nsGkAtoms::accent_, // accent
nsGkAtoms::accentunder_, // accentunder
nsGkAtoms::actiontype_, // actiontype
nsGkAtoms::align, // align
nsGkAtoms::alignmentscope_, // alignmentscope
nsGkAtoms::alt, // alt
nsGkAtoms::altimg_, // altimg
nsGkAtoms::altimg_height_, // altimg-height
nsGkAtoms::altimg_valign_, // altimg-valign
nsGkAtoms::altimg_width_, // altimg-width
nsGkAtoms::background, // background
nsGkAtoms::base, // base
nsGkAtoms::bevelled_, // bevelled
nsGkAtoms::cd_, // cd
nsGkAtoms::cdgroup_, // cdgroup
nsGkAtoms::charalign_, // charalign
nsGkAtoms::close, // close
nsGkAtoms::closure_, // closure
nsGkAtoms::color, // color
nsGkAtoms::columnalign_, // columnalign
nsGkAtoms::columnalignment_, // columnalignment
nsGkAtoms::columnlines_, // columnlines
nsGkAtoms::columnspacing_, // columnspacing
nsGkAtoms::columnspan_, // columnspan
nsGkAtoms::columnwidth_, // columnwidth
nsGkAtoms::crossout_, // crossout
nsGkAtoms::decimalpoint_, // decimalpoint
nsGkAtoms::definitionURL_, // definitionURL
nsGkAtoms::denomalign_, // denomalign
nsGkAtoms::depth_, // depth
nsGkAtoms::dir, // dir
nsGkAtoms::display, // display
nsGkAtoms::displaystyle_, // displaystyle
nsGkAtoms::edge_, // edge
nsGkAtoms::encoding, // encoding
nsGkAtoms::equalcolumns_, // equalcolumns
nsGkAtoms::equalrows_, // equalrows
nsGkAtoms::fence_, // fence
nsGkAtoms::fontfamily_, // fontfamily
nsGkAtoms::fontsize_, // fontsize
nsGkAtoms::fontstyle_, // fontstyle
nsGkAtoms::fontweight_, // fontweight
nsGkAtoms::form, // form
nsGkAtoms::frame, // frame
nsGkAtoms::framespacing_, // framespacing
nsGkAtoms::groupalign_, // groupalign
nsGkAtoms::height, // height
nsGkAtoms::href, // href
nsGkAtoms::id, // id
nsGkAtoms::indentalign_, // indentalign
nsGkAtoms::indentalignfirst_, // indentalignfirst
nsGkAtoms::indentalignlast_, // indentalignlast
nsGkAtoms::indentshift_, // indentshift
nsGkAtoms::indentshiftfirst_, // indentshiftfirst
nsGkAtoms::indenttarget_, // indenttarget
nsGkAtoms::index, // index
nsGkAtoms::integer, // integer
nsGkAtoms::largeop_, // largeop
nsGkAtoms::length, // length
nsGkAtoms::linebreak_, // linebreak
nsGkAtoms::linebreakmultchar_, // linebreakmultchar
nsGkAtoms::linebreakstyle_, // linebreakstyle
nsGkAtoms::linethickness_, // linethickness
nsGkAtoms::location_, // location
nsGkAtoms::longdivstyle_, // longdivstyle
nsGkAtoms::lquote_, // lquote
nsGkAtoms::lspace_, // lspace
nsGkAtoms::ltr, // ltr
nsGkAtoms::mathbackground_, // mathbackground
nsGkAtoms::mathcolor_, // mathcolor
nsGkAtoms::mathsize_, // mathsize
nsGkAtoms::mathvariant_, // mathvariant
nsGkAtoms::maxsize_, // maxsize
nsGkAtoms::minlabelspacing_, // minlabelspacing
nsGkAtoms::minsize_, // minsize
nsGkAtoms::movablelimits_, // movablelimits
nsGkAtoms::msgroup_, // msgroup
nsGkAtoms::name, // name
nsGkAtoms::newline, // newline
nsGkAtoms::notation_, // notation
nsGkAtoms::numalign_, // numalign
nsGkAtoms::number, // number
nsGkAtoms::open, // open
nsGkAtoms::order, // order
nsGkAtoms::other, // other
nsGkAtoms::overflow, // overflow
nsGkAtoms::position, // position
nsGkAtoms::role, // role
nsGkAtoms::rowalign_, // rowalign
nsGkAtoms::rowlines_, // rowlines
nsGkAtoms::rowspacing_, // rowspacing
nsGkAtoms::rowspan, // rowspan
nsGkAtoms::rquote_, // rquote
nsGkAtoms::rspace_, // rspace
nsGkAtoms::schemaLocation_, // schemaLocation
nsGkAtoms::scriptlevel_, // scriptlevel
nsGkAtoms::scriptminsize_, // scriptminsize
nsGkAtoms::scriptsize_, // scriptsize
nsGkAtoms::scriptsizemultiplier_, // scriptsizemultiplier
nsGkAtoms::selection_, // selection
nsGkAtoms::separator_, // separator
nsGkAtoms::separators_, // separators
nsGkAtoms::shift_, // shift
nsGkAtoms::side_, // side
nsGkAtoms::src, // src
nsGkAtoms::stackalign_, // stackalign
nsGkAtoms::stretchy_, // stretchy
nsGkAtoms::subscriptshift_, // subscriptshift
nsGkAtoms::superscriptshift_, // superscriptshift
nsGkAtoms::symmetric_, // symmetric
nsGkAtoms::type, // type
nsGkAtoms::voffset_, // voffset
nsGkAtoms::width, // width
nsGkAtoms::xref_, // xref
nullptr};
const nsStaticAtom* const kURLAttributesMathML[] = {
// clang-format off
nsGkAtoms::href,
nsGkAtoms::src,
nsGkAtoms::cdgroup_,
nsGkAtoms::altimg_,
nsGkAtoms::definitionURL_,
nullptr
// clang-format on
};
// https://wicg.github.io/sanitizer-api/#baseline-attribute-allow-list
constexpr const nsStaticAtom* const kBaselineAttributeAllowlist[] = {
// clang-format off
nsGkAtoms::abbr,
nsGkAtoms::accept,
nsGkAtoms::acceptcharset,
nsGkAtoms::charset,
nsGkAtoms::accesskey,
nsGkAtoms::action,
nsGkAtoms::align,
nsGkAtoms::alink,
nsGkAtoms::allow,
nsGkAtoms::allowfullscreen,
// nsGkAtoms::allowpaymentrequest,
nsGkAtoms::alt,
nsGkAtoms::anchor,
nsGkAtoms::archive,
nsGkAtoms::as,
nsGkAtoms::async,
nsGkAtoms::autocapitalize,
nsGkAtoms::autocomplete,
// nsGkAtoms::autocorrect,
nsGkAtoms::autofocus,
// nsGkAtoms::autopictureinpicture,
nsGkAtoms::autoplay,
nsGkAtoms::axis,
nsGkAtoms::background,
nsGkAtoms::behavior,
nsGkAtoms::bgcolor,
nsGkAtoms::border,
nsGkAtoms::bordercolor,
nsGkAtoms::capture,
nsGkAtoms::cellpadding,
nsGkAtoms::cellspacing,
// nsGkAtoms::challenge,
nsGkAtoms::_char,
nsGkAtoms::charoff,
nsGkAtoms::charset,
nsGkAtoms::checked,
nsGkAtoms::cite,
nsGkAtoms::_class,
nsGkAtoms::classid,
nsGkAtoms::clear,
nsGkAtoms::code,
nsGkAtoms::codebase,
nsGkAtoms::codetype,
nsGkAtoms::color,
nsGkAtoms::cols,
nsGkAtoms::colspan,
nsGkAtoms::compact,
nsGkAtoms::content,
nsGkAtoms::contenteditable,
nsGkAtoms::controls,
// nsGkAtoms::controlslist,
// nsGkAtoms::conversiondestination,
nsGkAtoms::coords,
nsGkAtoms::crossorigin,
nsGkAtoms::csp,
nsGkAtoms::data,
nsGkAtoms::datetime,
nsGkAtoms::declare,
nsGkAtoms::decoding,
nsGkAtoms::_default,
nsGkAtoms::defer,
nsGkAtoms::dir,
nsGkAtoms::direction,
// nsGkAtoms::dirname,
nsGkAtoms::disabled,
// nsGkAtoms::disablepictureinpicture,
// nsGkAtoms::disableremoteplayback,
// nsGkAtoms::disallowdocumentaccess,
nsGkAtoms::download,
nsGkAtoms::draggable,
// nsGkAtoms::elementtiming,
nsGkAtoms::enctype,
nsGkAtoms::end,
nsGkAtoms::enterkeyhint,
nsGkAtoms::event,
nsGkAtoms::exportparts,
nsGkAtoms::face,
nsGkAtoms::_for,
nsGkAtoms::form,
nsGkAtoms::formaction,
nsGkAtoms::formenctype,
nsGkAtoms::formmethod,
nsGkAtoms::formnovalidate,
nsGkAtoms::formtarget,
nsGkAtoms::frame,
nsGkAtoms::frameborder,
nsGkAtoms::headers,
nsGkAtoms::height,
nsGkAtoms::hidden,
nsGkAtoms::high,
nsGkAtoms::href,
nsGkAtoms::hreflang,
// nsGkAtoms::hreftranslate,
nsGkAtoms::hspace,
nsGkAtoms::http,
// nsGkAtoms::equiv,
nsGkAtoms::id,
nsGkAtoms::imagesizes,
nsGkAtoms::imagesrcset,
// nsGkAtoms::importance,
// nsGkAtoms::impressiondata,
// nsGkAtoms::impressionexpiry,
// nsGkAtoms::incremental,
nsGkAtoms::inert,
nsGkAtoms::inputmode,
nsGkAtoms::integrity,
// nsGkAtoms::invisible,
nsGkAtoms::is,
nsGkAtoms::ismap,
// nsGkAtoms::keytype,
nsGkAtoms::kind,
nsGkAtoms::label,
nsGkAtoms::lang,
nsGkAtoms::language,
// nsGkAtoms::latencyhint,
nsGkAtoms::leftmargin,
nsGkAtoms::link,
// nsGkAtoms::list,
nsGkAtoms::loading,
nsGkAtoms::longdesc,
nsGkAtoms::loop,
nsGkAtoms::low,
nsGkAtoms::lowsrc,
nsGkAtoms::manifest,
nsGkAtoms::marginheight,
nsGkAtoms::marginwidth,
nsGkAtoms::max,
nsGkAtoms::maxlength,
// nsGkAtoms::mayscript,
nsGkAtoms::media,
nsGkAtoms::method,
nsGkAtoms::min,
nsGkAtoms::minlength,
nsGkAtoms::multiple,
nsGkAtoms::muted,
nsGkAtoms::name,
nsGkAtoms::nohref,
nsGkAtoms::nomodule,
nsGkAtoms::nonce,
nsGkAtoms::noresize,
nsGkAtoms::noshade,
nsGkAtoms::novalidate,
nsGkAtoms::nowrap,
nsGkAtoms::object,
nsGkAtoms::open,
nsGkAtoms::optimum,
nsGkAtoms::part,
nsGkAtoms::pattern,
nsGkAtoms::ping,
nsGkAtoms::placeholder,
// nsGkAtoms::playsinline,
// nsGkAtoms::policy,
nsGkAtoms::poster,
nsGkAtoms::preload,
// nsGkAtoms::pseudo,
nsGkAtoms::readonly,
nsGkAtoms::referrerpolicy,
nsGkAtoms::rel,
// nsGkAtoms::reportingorigin,
nsGkAtoms::required,
nsGkAtoms::resources,
nsGkAtoms::rev,
nsGkAtoms::reversed,
nsGkAtoms::role,
nsGkAtoms::rows,
nsGkAtoms::rowspan,
nsGkAtoms::rules,
nsGkAtoms::sandbox,
nsGkAtoms::scheme,
nsGkAtoms::scope,
// nsGkAtoms::scopes,
nsGkAtoms::scrollamount,
nsGkAtoms::scrolldelay,
nsGkAtoms::scrolling,
nsGkAtoms::select,
nsGkAtoms::selected,
// nsGkAtoms::shadowroot,
// nsGkAtoms::shadowrootdelegatesfocus,
nsGkAtoms::shape,
nsGkAtoms::size,
nsGkAtoms::sizes,
nsGkAtoms::slot,
nsGkAtoms::span,
nsGkAtoms::spellcheck,
nsGkAtoms::src,
nsGkAtoms::srcdoc,
nsGkAtoms::srclang,
nsGkAtoms::srcset,
nsGkAtoms::standby,
nsGkAtoms::start,
nsGkAtoms::step,
nsGkAtoms::style,
nsGkAtoms::summary,
nsGkAtoms::tabindex,
nsGkAtoms::target,
nsGkAtoms::text,
nsGkAtoms::title,
nsGkAtoms::topmargin,
nsGkAtoms::translate,
nsGkAtoms::truespeed,
// nsGkAtoms::trusttoken,
nsGkAtoms::type,
nsGkAtoms::usemap,
nsGkAtoms::valign,
nsGkAtoms::value,
nsGkAtoms::valuetype,
nsGkAtoms::version,
// nsGkAtoms::virtualkeyboardpolicy,
nsGkAtoms::vlink,
nsGkAtoms::vspace,
nsGkAtoms::webkitdirectory,
nsGkAtoms::width,
nsGkAtoms::wrap,
// clang-format on
};
// https://wicg.github.io/sanitizer-api/#baseline-elements
constexpr const nsStaticAtom* const kBaselineElementAllowlist[] = {
nsGkAtoms::a, nsGkAtoms::abbr, nsGkAtoms::acronym,
nsGkAtoms::address, nsGkAtoms::area, nsGkAtoms::article,
nsGkAtoms::aside, nsGkAtoms::audio, nsGkAtoms::b,
nsGkAtoms::basefont, nsGkAtoms::bdi, nsGkAtoms::bdo,
nsGkAtoms::bgsound, nsGkAtoms::big, nsGkAtoms::blockquote,
nsGkAtoms::body, nsGkAtoms::br, nsGkAtoms::button,
nsGkAtoms::canvas, nsGkAtoms::caption, nsGkAtoms::center,
nsGkAtoms::cite, nsGkAtoms::code, nsGkAtoms::col,
nsGkAtoms::colgroup, nsGkAtoms::command, nsGkAtoms::data,
nsGkAtoms::datalist, nsGkAtoms::dd, nsGkAtoms::del,
nsGkAtoms::details, nsGkAtoms::dfn, nsGkAtoms::dialog,
nsGkAtoms::dir, nsGkAtoms::div, nsGkAtoms::dl,
nsGkAtoms::dt, nsGkAtoms::em, nsGkAtoms::fieldset,
nsGkAtoms::figcaption, nsGkAtoms::figure, nsGkAtoms::font,
nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1,
nsGkAtoms::h2, nsGkAtoms::h3, nsGkAtoms::h4,
nsGkAtoms::h5, nsGkAtoms::h6, nsGkAtoms::head,
nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr,
nsGkAtoms::html, nsGkAtoms::i, nsGkAtoms::image,
nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::ins,
nsGkAtoms::kbd, nsGkAtoms::keygen, nsGkAtoms::label,
nsGkAtoms::layer, nsGkAtoms::legend, nsGkAtoms::li,
nsGkAtoms::link, nsGkAtoms::listing, nsGkAtoms::main,
nsGkAtoms::map, nsGkAtoms::mark, nsGkAtoms::marquee,
nsGkAtoms::menu, nsGkAtoms::meta, nsGkAtoms::meter,
nsGkAtoms::nav, nsGkAtoms::nobr, nsGkAtoms::ol,
nsGkAtoms::optgroup, nsGkAtoms::option, nsGkAtoms::output,
nsGkAtoms::p, nsGkAtoms::picture, nsGkAtoms::plaintext,
nsGkAtoms::popup, nsGkAtoms::portal, nsGkAtoms::pre,
nsGkAtoms::progress, nsGkAtoms::q, nsGkAtoms::rb,
nsGkAtoms::rp, nsGkAtoms::rt, nsGkAtoms::rtc,
nsGkAtoms::ruby, nsGkAtoms::s, nsGkAtoms::samp,
nsGkAtoms::section, nsGkAtoms::select, nsGkAtoms::selectmenu,
nsGkAtoms::slot, nsGkAtoms::small, nsGkAtoms::source,
nsGkAtoms::span, nsGkAtoms::strike, nsGkAtoms::strong,
nsGkAtoms::style, nsGkAtoms::sub, nsGkAtoms::summary,
nsGkAtoms::sup, nsGkAtoms::table, nsGkAtoms::tbody,
nsGkAtoms::td, nsGkAtoms::_template, nsGkAtoms::textarea,
nsGkAtoms::tfoot, nsGkAtoms::th, nsGkAtoms::thead,
nsGkAtoms::time, nsGkAtoms::title, nsGkAtoms::tr,
nsGkAtoms::track, nsGkAtoms::tt, nsGkAtoms::u,
nsGkAtoms::ul, nsGkAtoms::var, nsGkAtoms::video,
nsGkAtoms::wbr, nsGkAtoms::xmp,
};
// https://wicg.github.io/sanitizer-api/#default-configuration
// default configuration's attribute allow list.
// Note: Currently all listed attributes are allowed for every element
// (e.g. they use "*").
// Compared to kBaselineAttributeAllowlist only deprecated allowpaymentrequest
// attribute is missing.
constexpr const nsStaticAtom* const kDefaultConfigurationAttributeAllowlist[] =
{
nsGkAtoms::abbr,
nsGkAtoms::accept,
nsGkAtoms::acceptcharset,
nsGkAtoms::charset,
nsGkAtoms::accesskey,
nsGkAtoms::action,
nsGkAtoms::align,
nsGkAtoms::alink,
nsGkAtoms::allow,
nsGkAtoms::allowfullscreen,
nsGkAtoms::alt,
nsGkAtoms::anchor,
nsGkAtoms::archive,
nsGkAtoms::as,
nsGkAtoms::async,
nsGkAtoms::autocapitalize,
nsGkAtoms::autocomplete,
// nsGkAtoms::autocorrect,
nsGkAtoms::autofocus,
// nsGkAtoms::autopictureinpicture,
nsGkAtoms::autoplay,
nsGkAtoms::axis,
nsGkAtoms::background,
nsGkAtoms::behavior,
nsGkAtoms::bgcolor,
nsGkAtoms::border,
nsGkAtoms::bordercolor,
nsGkAtoms::capture,
nsGkAtoms::cellpadding,
nsGkAtoms::cellspacing,
// nsGkAtoms::challenge,
nsGkAtoms::_char,
nsGkAtoms::charoff,
nsGkAtoms::charset,
nsGkAtoms::checked,
nsGkAtoms::cite,
nsGkAtoms::_class,
nsGkAtoms::classid,
nsGkAtoms::clear,
nsGkAtoms::code,
nsGkAtoms::codebase,
nsGkAtoms::codetype,
nsGkAtoms::color,
nsGkAtoms::cols,
nsGkAtoms::colspan,
nsGkAtoms::compact,
nsGkAtoms::content,
nsGkAtoms::contenteditable,
nsGkAtoms::controls,
// nsGkAtoms::controlslist,
// nsGkAtoms::conversiondestination,
nsGkAtoms::coords,
nsGkAtoms::crossorigin,
nsGkAtoms::csp,
nsGkAtoms::data,
nsGkAtoms::datetime,
nsGkAtoms::declare,
nsGkAtoms::decoding,
nsGkAtoms::_default,
nsGkAtoms::defer,
nsGkAtoms::dir,
nsGkAtoms::direction,
// nsGkAtoms::dirname,
nsGkAtoms::disabled,
// nsGkAtoms::disablepictureinpicture,
// nsGkAtoms::disableremoteplayback,
// nsGkAtoms::disallowdocumentaccess,
nsGkAtoms::download,
nsGkAtoms::draggable,
// nsGkAtoms::elementtiming,
nsGkAtoms::enctype,
nsGkAtoms::end,
nsGkAtoms::enterkeyhint,
nsGkAtoms::event,
nsGkAtoms::exportparts,
nsGkAtoms::face,
nsGkAtoms::_for,
nsGkAtoms::form,
nsGkAtoms::formaction,
nsGkAtoms::formenctype,
nsGkAtoms::formmethod,
nsGkAtoms::formnovalidate,
nsGkAtoms::formtarget,
nsGkAtoms::frame,
nsGkAtoms::frameborder,
nsGkAtoms::headers,
nsGkAtoms::height,
nsGkAtoms::hidden,
nsGkAtoms::high,
nsGkAtoms::href,
nsGkAtoms::hreflang,
// nsGkAtoms::hreftranslate,
nsGkAtoms::hspace,
nsGkAtoms::http,
// nsGkAtoms::equiv,
nsGkAtoms::id,
nsGkAtoms::imagesizes,
nsGkAtoms::imagesrcset,
// nsGkAtoms::importance,
// nsGkAtoms::impressiondata,
// nsGkAtoms::impressionexpiry,
// nsGkAtoms::incremental,
nsGkAtoms::inert,
nsGkAtoms::inputmode,
nsGkAtoms::integrity,
// nsGkAtoms::invisible,
nsGkAtoms::is,
nsGkAtoms::ismap,
// nsGkAtoms::keytype,
nsGkAtoms::kind,
nsGkAtoms::label,
nsGkAtoms::lang,
nsGkAtoms::language,
// nsGkAtoms::latencyhint,
nsGkAtoms::leftmargin,
nsGkAtoms::link,
// nsGkAtoms::list,
nsGkAtoms::loading,
nsGkAtoms::longdesc,
nsGkAtoms::loop,
nsGkAtoms::low,
nsGkAtoms::lowsrc,
nsGkAtoms::manifest,
nsGkAtoms::marginheight,
nsGkAtoms::marginwidth,
nsGkAtoms::max,
nsGkAtoms::maxlength,
// nsGkAtoms::mayscript,
nsGkAtoms::media,
nsGkAtoms::method,
nsGkAtoms::min,
nsGkAtoms::minlength,
nsGkAtoms::multiple,
nsGkAtoms::muted,
nsGkAtoms::name,
nsGkAtoms::nohref,
nsGkAtoms::nomodule,
nsGkAtoms::nonce,
nsGkAtoms::noresize,
nsGkAtoms::noshade,
nsGkAtoms::novalidate,
nsGkAtoms::nowrap,
nsGkAtoms::object,
nsGkAtoms::open,
nsGkAtoms::optimum,
nsGkAtoms::part,
nsGkAtoms::pattern,
nsGkAtoms::ping,
nsGkAtoms::placeholder,
// nsGkAtoms::playsinline,
// nsGkAtoms::policy,
nsGkAtoms::poster,
nsGkAtoms::preload,
// nsGkAtoms::pseudo,
nsGkAtoms::readonly,
nsGkAtoms::referrerpolicy,
nsGkAtoms::rel,
// nsGkAtoms::reportingorigin,
nsGkAtoms::required,
nsGkAtoms::resources,
nsGkAtoms::rev,
nsGkAtoms::reversed,
nsGkAtoms::role,
nsGkAtoms::rows,
nsGkAtoms::rowspan,
nsGkAtoms::rules,
nsGkAtoms::sandbox,
nsGkAtoms::scheme,
nsGkAtoms::scope,
// nsGkAtoms::scopes,
nsGkAtoms::scrollamount,
nsGkAtoms::scrolldelay,
nsGkAtoms::scrolling,
nsGkAtoms::select,
nsGkAtoms::selected,
// nsGkAtoms::shadowroot,
// nsGkAtoms::shadowrootdelegatesfocus,
nsGkAtoms::shape,
nsGkAtoms::size,
nsGkAtoms::sizes,
nsGkAtoms::slot,
nsGkAtoms::span,
nsGkAtoms::spellcheck,
nsGkAtoms::src,
nsGkAtoms::srcdoc,
nsGkAtoms::srclang,
nsGkAtoms::srcset,
nsGkAtoms::standby,
nsGkAtoms::start,
nsGkAtoms::step,
nsGkAtoms::style,
nsGkAtoms::summary,
nsGkAtoms::tabindex,
nsGkAtoms::target,
nsGkAtoms::text,
nsGkAtoms::title,
nsGkAtoms::topmargin,
nsGkAtoms::translate,
nsGkAtoms::truespeed,
// nsGkAtoms::trusttoken,
nsGkAtoms::type,
nsGkAtoms::usemap,
nsGkAtoms::valign,
nsGkAtoms::value,
nsGkAtoms::valuetype,
nsGkAtoms::version,
// nsGkAtoms::virtualkeyboardpolicy,
nsGkAtoms::vlink,
nsGkAtoms::vspace,
nsGkAtoms::webkitdirectory,
nsGkAtoms::width,
nsGkAtoms::wrap,
};
// https://wicg.github.io/sanitizer-api/#default-configuration
// default configuration's element allow list.
constexpr const nsStaticAtom* const kDefaultConfigurationElementAllowlist[] = {
nsGkAtoms::a, nsGkAtoms::abbr, nsGkAtoms::acronym,
nsGkAtoms::address, nsGkAtoms::area, nsGkAtoms::article,
nsGkAtoms::aside, nsGkAtoms::audio, nsGkAtoms::b,
nsGkAtoms::bdi, nsGkAtoms::bdo, nsGkAtoms::bgsound,
nsGkAtoms::big, nsGkAtoms::blockquote, nsGkAtoms::body,
nsGkAtoms::br, nsGkAtoms::button, nsGkAtoms::canvas,
nsGkAtoms::caption, nsGkAtoms::center, nsGkAtoms::cite,
nsGkAtoms::code, nsGkAtoms::col, nsGkAtoms::colgroup,
nsGkAtoms::datalist, nsGkAtoms::dd, nsGkAtoms::del,
nsGkAtoms::details, nsGkAtoms::dfn, nsGkAtoms::dialog,
nsGkAtoms::dir, nsGkAtoms::div, nsGkAtoms::dl,
nsGkAtoms::dt, nsGkAtoms::em, nsGkAtoms::fieldset,
nsGkAtoms::figcaption, nsGkAtoms::figure, nsGkAtoms::font,
nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1,
nsGkAtoms::h2, nsGkAtoms::h3, nsGkAtoms::h4,
nsGkAtoms::h5, nsGkAtoms::h6, nsGkAtoms::head,
nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr,
nsGkAtoms::html, nsGkAtoms::i, nsGkAtoms::img,
nsGkAtoms::input, nsGkAtoms::ins, nsGkAtoms::kbd,
nsGkAtoms::keygen, nsGkAtoms::label, nsGkAtoms::layer,
nsGkAtoms::legend, nsGkAtoms::li, nsGkAtoms::link,
nsGkAtoms::listing, nsGkAtoms::main, nsGkAtoms::map,
nsGkAtoms::mark, nsGkAtoms::marquee, nsGkAtoms::menu,
nsGkAtoms::meta, nsGkAtoms::meter, nsGkAtoms::nav,
nsGkAtoms::nobr, nsGkAtoms::ol, nsGkAtoms::optgroup,
nsGkAtoms::option, nsGkAtoms::output, nsGkAtoms::p,
nsGkAtoms::picture, nsGkAtoms::popup, nsGkAtoms::pre,
nsGkAtoms::progress, nsGkAtoms::q, nsGkAtoms::rb,
nsGkAtoms::rp, nsGkAtoms::rt, nsGkAtoms::rtc,
nsGkAtoms::ruby, nsGkAtoms::s, nsGkAtoms::samp,
nsGkAtoms::section, nsGkAtoms::select, nsGkAtoms::selectmenu,
nsGkAtoms::small, nsGkAtoms::source, nsGkAtoms::span,
nsGkAtoms::strike, nsGkAtoms::strong, nsGkAtoms::style,
nsGkAtoms::sub, nsGkAtoms::summary, nsGkAtoms::sup,
nsGkAtoms::table, nsGkAtoms::tbody, nsGkAtoms::td,
nsGkAtoms::tfoot, nsGkAtoms::th, nsGkAtoms::thead,
nsGkAtoms::time, nsGkAtoms::tr, nsGkAtoms::track,
nsGkAtoms::tt, nsGkAtoms::u, nsGkAtoms::ul,
nsGkAtoms::var, nsGkAtoms::video, nsGkAtoms::wbr,
};
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sElementsHTML = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sAttributesHTML = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sPresAttributesHTML = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sElementsSVG = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sAttributesSVG = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sElementsMathML = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sAttributesMathML = nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sBaselineAttributeAllowlist =
nullptr;
nsTreeSanitizer::AtomsTable* nsTreeSanitizer::sBaselineElementAllowlist =
nullptr;
nsTreeSanitizer::AtomsTable*
nsTreeSanitizer::sDefaultConfigurationAttributeAllowlist = nullptr;
nsTreeSanitizer::AtomsTable*
nsTreeSanitizer::sDefaultConfigurationElementAllowlist = nullptr;
nsIPrincipal* nsTreeSanitizer::sNullPrincipal = nullptr;
nsTreeSanitizer::nsTreeSanitizer(uint32_t aFlags)
: mAllowStyles(aFlags & nsIParserUtils::SanitizerAllowStyle),
mAllowComments(aFlags & nsIParserUtils::SanitizerAllowComments),
mDropNonCSSPresentation(aFlags &
nsIParserUtils::SanitizerDropNonCSSPresentation),
mDropForms(aFlags & nsIParserUtils::SanitizerDropForms),
mCidEmbedsOnly(aFlags & nsIParserUtils::SanitizerCidEmbedsOnly),
mDropMedia(aFlags & nsIParserUtils::SanitizerDropMedia),
mFullDocument(false),
mLogRemovals(aFlags & nsIParserUtils::SanitizerLogRemovals) {
if (mCidEmbedsOnly) {
// Sanitizing styles for external references is not supported.
mAllowStyles = false;
}
if (!sElementsHTML) {
// Initialize lazily to avoid having to initialize at all if the user
// doesn't paste HTML or load feeds.
InitializeStatics();
}
}
bool nsTreeSanitizer::MustFlatten(int32_t aNamespace, nsAtom* aLocal) {
if (mIsForSanitizerAPI) {
return MustFlattenForSanitizerAPI(aNamespace, aLocal);
}
if (aNamespace == kNameSpaceID_XHTML) {
if (mDropNonCSSPresentation &&
(nsGkAtoms::font == aLocal || nsGkAtoms::center == aLocal)) {
return true;
}
if (mDropForms &&
(nsGkAtoms::form == aLocal || nsGkAtoms::input == aLocal ||
nsGkAtoms::option == aLocal || nsGkAtoms::optgroup == aLocal)) {
return true;
}
if (mFullDocument &&
(nsGkAtoms::title == aLocal || nsGkAtoms::html == aLocal ||
nsGkAtoms::head == aLocal || nsGkAtoms::body == aLocal)) {
return false;
}
if (nsGkAtoms::_template == aLocal) {
return false;
}
return !sElementsHTML->Contains(aLocal);
}
if (aNamespace == kNameSpaceID_SVG) {
if (mCidEmbedsOnly || mDropMedia) {
// Sanitizing CSS-based URL references inside SVG presentational
// attributes is not supported, so flattening for cid: embed case.
return true;
}
return !sElementsSVG->Contains(aLocal);
}
if (aNamespace == kNameSpaceID_MathML) {
return !sElementsMathML->Contains(aLocal);
}
return true;
}
bool nsTreeSanitizer::MustFlattenForSanitizerAPI(int32_t aNamespace,
nsAtom* aLocal) {
// This implements everything in
// https://wicg.github.io/sanitizer-api/#sanitize-action-for-an-element that
// is supposed to be blocked.
// Step 6. If element matches any name in config["blockElements"]: Return
// block.
if (mReplaceWithChildrenElements &&
MatchesElementName(*mReplaceWithChildrenElements, aNamespace, aLocal)) {
return true;
}
// Step 7. Let allow list be null.
// Step 8. If "allowElements" exists in config:
// Step 8.1. Then : Set allow list to config["allowElements"].
if (mElements) {
// Step 9. If element does not match any name in allow list:
// Return block.
if (!mElements->Contains(ElementName(aNamespace, aLocal))) {
return true;
}
} else {
// Step 8.2. Otherwise: Set allow list to the default configuration's
// element allow list.
// Step 9. If element does not match any name in allow list:
// Return block.
// The default configuration only contains HTML elements, so we can
// reject everything else.
if (aNamespace != kNameSpaceID_XHTML ||
!sDefaultConfigurationElementAllowlist->Contains(aLocal)) {
return true;
}
}
// Step 10. Return keep.
return false;
}
bool nsTreeSanitizer::IsURL(const nsStaticAtom* const* aURLs,
nsAtom* aLocalName) {
const nsStaticAtom* atom;
while ((atom = *aURLs)) {
if (atom == aLocalName) {
return true;
}
++aURLs;
}
return false;
}
bool nsTreeSanitizer::MustPrune(int32_t aNamespace, nsAtom* aLocal,
mozilla::dom::Element* aElement) {
if (mIsForSanitizerAPI) {
return MustPruneForSanitizerAPI(aNamespace, aLocal, aElement);
}
// To avoid attacks where a MathML script becomes something that gets
// serialized in a way that it parses back as an HTML script, let's just
// drop elements with the local name 'script' regardless of namespace.
if (nsGkAtoms::script == aLocal) {
return true;
}
if (aNamespace == kNameSpaceID_XHTML) {
if (nsGkAtoms::title == aLocal && !mFullDocument) {
// emulate the quirks of the old parser
return true;
}
if (mDropForms &&
(nsGkAtoms::select == aLocal || nsGkAtoms::button == aLocal ||
nsGkAtoms::datalist == aLocal)) {
return true;
}
if (mDropMedia &&
(nsGkAtoms::img == aLocal || nsGkAtoms::video == aLocal ||
nsGkAtoms::audio == aLocal || nsGkAtoms::source == aLocal)) {
return true;
}
if (nsGkAtoms::meta == aLocal &&
(aElement->HasAttr(nsGkAtoms::charset) ||
aElement->HasAttr(nsGkAtoms::httpEquiv))) {
// Throw away charset declarations even if they also have microdata
// which they can't validly have.
return true;
}
if (((!mFullDocument && nsGkAtoms::meta == aLocal) ||
nsGkAtoms::link == aLocal) &&
!(aElement->HasAttr(nsGkAtoms::itemprop) ||
aElement->HasAttr(nsGkAtoms::itemscope))) {
// emulate old behavior for non-Microdata <meta> and <link> presumably
// in <head>. <meta> and <link> are whitelisted in order to avoid
// corrupting Microdata when they appear in <body>. Note that
// SanitizeAttributes() will remove the rel attribute from <link> and
// the name attribute from <meta>.
return true;
}
}
if (mAllowStyles) {
return nsGkAtoms::style == aLocal && !(aNamespace == kNameSpaceID_XHTML ||
aNamespace == kNameSpaceID_SVG);
}
if (nsGkAtoms::style == aLocal) {
return true;
}
return false;
}
enum class ElementKind {
Regular,
Custom,
Unknown,
};
// https://wicg.github.io/sanitizer-api/#element-kind
static ElementKind GetElementKind(int32_t aNamespace, nsAtom* aLocal,
Element* aElement) {
// XXX(bug 1782926) The spec for this is known to be wrong.
// https://github.com/WICG/sanitizer-api/issues/147
// custom, if elements local name is a valid custom element name,
// XXX shouldn't this happen after unknown.
if (nsContentUtils::IsCustomElementName(aLocal, kNameSpaceID_XHTML)) {
return ElementKind::Custom;
}
// unknown, if element is not in the [HTML] namespace
// XXX this doesn't really make sense to me
// https://github.com/WICG/sanitizer-api/issues/167
if (aNamespace != kNameSpaceID_XHTML) {
return ElementKind::Unknown;
}
// or if elements local name denotes an unknown element
// — that is, if the element interface the [HTML] specification assigns to it
// would be HTMLUnknownElement,
if (nsCOMPtr<HTMLUnknownElement> el = do_QueryInterface(aElement)) {
return ElementKind::Unknown;
}
// regular, otherwise.
return ElementKind::Regular;
}
bool nsTreeSanitizer::MustPruneForSanitizerAPI(int32_t aNamespace,
nsAtom* aLocal,
Element* aElement) {
// This implements everything in
// https://wicg.github.io/sanitizer-api/#sanitize-action-for-an-element that
// is supposed to be dropped.
// Step 1. Let kind be elements element kind.
ElementKind kind = GetElementKind(aNamespace, aLocal, aElement);
switch (kind) {
case ElementKind::Regular:
// Step 2. If kind is regular and element does not match any name in the
// baseline element allow list: Return drop.
if (!sBaselineElementAllowlist->Contains(aLocal)) {
return true;
}
break;
case ElementKind::Custom:
// Step 3. If kind is custom and if config["allowCustomElements"] does not
// exist or if config["allowCustomElements"] is false: Return drop.
if (!mAllowCustomElements) {
return true;
}
break;
case ElementKind::Unknown:
// Step 4. If kind is unknown and if config["allowUnknownMarkup"] does not
// exist or it config["allowUnknownMarkup"] is false: Return drop.
if (!mAllowUnknownMarkup) {
return true;
}
break;
}
// Step 5. If element matches any name in config["dropElements"]: Return drop.
if (mRemoveElements &&
MatchesElementName(*mRemoveElements, aNamespace, aLocal)) {
return true;
}
return false;
}
/**
* Parses a style sheet and reserializes it with unsafe styles removed.
*
* @param aOriginal the original style sheet source
* @param aSanitized the reserialization without dangerous CSS.
* @param aDocument the document the style sheet belongs to
* @param aBaseURI the base URI to use
* @param aSanitizationKind the kind of style sanitization to use.
*/
static void SanitizeStyleSheet(const nsAString& aOriginal,
nsAString& aSanitized, Document* aDocument,
nsIURI* aBaseURI,
StyleSanitizationKind aSanitizationKind) {
aSanitized.Truncate();
NS_ConvertUTF16toUTF8 style(aOriginal);
nsIReferrerInfo* referrer =
aDocument->ReferrerInfoForInternalCSSAndSVGResources();
auto extraData =
MakeRefPtr<URLExtraData>(aBaseURI, referrer, aDocument->NodePrincipal());
RefPtr<StyleStylesheetContents> contents =
Servo_StyleSheet_FromUTF8Bytes(
/* loader = */ nullptr,
/* stylesheet = */ nullptr,
/* load_data = */ nullptr, &style,
css::SheetParsingMode::eAuthorSheetFeatures, extraData.get(),
aDocument->GetCompatibilityMode(),
/* reusable_sheets = */ nullptr,
/* use_counters = */ nullptr, StyleAllowImportRules::Yes,
aSanitizationKind, &aSanitized)
.Consume();
}
bool nsTreeSanitizer::SanitizeInlineStyle(
Element* aElement, StyleSanitizationKind aSanitizationKind) {
MOZ_ASSERT(aElement);
MOZ_ASSERT(aElement->IsHTMLElement(nsGkAtoms::style) ||
aElement->IsSVGElement(nsGkAtoms::style));
nsAutoString styleText;
nsContentUtils::GetNodeTextContent(aElement, false, styleText);
nsAutoString sanitizedStyle;
SanitizeStyleSheet(styleText, sanitizedStyle, aElement->OwnerDoc(),
aElement->GetBaseURI(), StyleSanitizationKind::Standard);
RemoveAllAttributesFromDescendants(aElement);
nsContentUtils::SetNodeTextContent(aElement, sanitizedStyle, true);
return sanitizedStyle.Length() != styleText.Length();
}
void nsTreeSanitizer::RemoveConditionalCSSFromSubtree(nsINode* aRoot) {
AutoTArray<RefPtr<nsINode>, 10> nodesToSanitize;
for (nsINode* node : ShadowIncludingTreeIterator(*aRoot)) {
if (node->IsHTMLElement(nsGkAtoms::style) ||
node->IsSVGElement(nsGkAtoms::style)) {
nodesToSanitize.AppendElement(node);
}
}
for (nsINode* node : nodesToSanitize) {
SanitizeInlineStyle(node->AsElement(),
StyleSanitizationKind::NoConditionalRules);
}
}
template <size_t Len>
static bool UTF16StringStartsWith(const char16_t* aStr, uint32_t aLength,
const char16_t (&aNeedle)[Len]) {
MOZ_ASSERT(aNeedle[Len - 1] == '\0',
"needle should be a UTF-16 encoded string literal");
if (aLength < Len - 1) {
return false;
}
for (size_t i = 0; i < Len - 1; i++) {
if (aStr[i] != aNeedle[i]) {
return false;
}
}
return true;
}
void nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement,
AllowedAttributes aAllowed) {
int32_t ac = (int)aElement->GetAttrCount();
for (int32_t i = ac - 1; i >= 0; --i) {
const nsAttrName* attrName = aElement->GetAttrNameAt(i);
int32_t attrNs = attrName->NamespaceID();
RefPtr<nsAtom> attrLocal = attrName->LocalName();
if (mIsForSanitizerAPI) {
if (MustDropAttribute(aElement, attrNs, attrLocal) ||
MustDropFunkyAttribute(aElement, attrNs, attrLocal)) {
aElement->UnsetAttr(kNameSpaceID_None, attrLocal, false);
if (mLogRemovals) {
LogMessage("Removed unsafe attribute.", aElement->OwnerDoc(),
aElement, attrLocal);
}
// in case the attribute removal shuffled the attribute order, start
// the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
}
continue;
}
if (kNameSpaceID_None == attrNs) {
if (aAllowed.mStyle && nsGkAtoms::style == attrLocal) {
continue;
}
if (aAllowed.mDangerousSrc && nsGkAtoms::src == attrLocal) {
continue;
}
if (IsURL(aAllowed.mURLs, attrLocal)) {
bool fragmentOnly = aElement->IsSVGElement(nsGkAtoms::use);
if (SanitizeURL(aElement, attrNs, attrLocal, fragmentOnly)) {
// in case the attribute removal shuffled the attribute order, start
// the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
continue;
}
// else fall through to see if there's another reason to drop this
// attribute (in particular if the attribute is background="" on an
// HTML element)
}
if (!mDropNonCSSPresentation &&
(aAllowed.mNames == sAttributesHTML) && // element is HTML
sPresAttributesHTML->Contains(attrLocal)) {
continue;
}
if (aAllowed.mNames->Contains(attrLocal) &&
!((attrLocal == nsGkAtoms::rel &&
aElement->IsHTMLElement(nsGkAtoms::link)) ||
(!mFullDocument && attrLocal == nsGkAtoms::name &&
aElement->IsHTMLElement(nsGkAtoms::meta)))) {
// name="" and rel="" are whitelisted, but treat them as blacklisted
// for <meta name> (fragment case) and <link rel> (all cases) to avoid
// document-wide metadata or styling overrides with non-conforming
// <meta name itemprop> or
// <link rel itemprop>
continue;
}
const char16_t* localStr = attrLocal->GetUTF16String();
uint32_t localLen = attrLocal->GetLength();
// Allow underscore to cater to the MCE editor library.
// Allow data-* on SVG and MathML, too, as a forward-compat measure.
// Allow aria-* on all for simplicity.
if (UTF16StringStartsWith(localStr, localLen, u"_") ||
UTF16StringStartsWith(localStr, localLen, u"data-") ||
UTF16StringStartsWith(localStr, localLen, u"aria-")) {
continue;
}
// else not allowed
} else if (kNameSpaceID_XML == attrNs) {
if (nsGkAtoms::lang == attrLocal || nsGkAtoms::space == attrLocal) {
continue;
}
// else not allowed
} else if (aAllowed.mXLink && kNameSpaceID_XLink == attrNs) {
if (nsGkAtoms::href == attrLocal) {
bool fragmentOnly = aElement->IsSVGElement(nsGkAtoms::use);
if (SanitizeURL(aElement, attrNs, attrLocal, fragmentOnly)) {
// in case the attribute removal shuffled the attribute order, start
// the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
}
continue;
}
if (nsGkAtoms::type == attrLocal || nsGkAtoms::title == attrLocal ||
nsGkAtoms::show == attrLocal || nsGkAtoms::actuate == attrLocal) {
continue;
}
// else not allowed
}
aElement->UnsetAttr(kNameSpaceID_None, attrLocal, false);
if (mLogRemovals) {
LogMessage("Removed unsafe attribute.", aElement->OwnerDoc(), aElement,
attrLocal);
}
// in case the attribute removal shuffled the attribute order, start the
// loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
}
// If we've got HTML audio or video, add the controls attribute, because
// otherwise the content is unplayable with scripts removed.
if (aElement->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::controls, u""_ns, false);
}
}
// https://wicg.github.io/sanitizer-api/#element-matches-an-element-name
bool nsTreeSanitizer::MatchesElementName(ElementNameSet& aNames,
int32_t aNamespace,
nsAtom* aLocalName) {
return aNames.Contains(ElementName(aNamespace, aLocalName));
}
bool nsTreeSanitizer::MatchesAttributeName(AttributeNameSet& aNames,
int32_t aNamespace,
nsAtom* aLocalName) {
return aNames.Contains(AttributeName(aNamespace, aLocalName));
}
// https://wicg.github.io/sanitizer-api/#sanitize-action-for-an-attribute
bool nsTreeSanitizer::MustDropAttribute(Element* aElement,
int32_t aAttrNamespace,
nsAtom* aAttrLocalName) {
// Step 1. Let kind be attributes attribute kind.
// Step 2. If kind is unknown and if config["allowUnknownMarkup"] does not
// exist or it config["allowUnknownMarkup"] is false: Return drop.
//
// TODO: Not clear how to determine if something is an "unknown" attribute.
// https://github.com/WICG/sanitizer-api/issues/147 should probably define
// an explicit list.
// Step 3. If kind is regular and attributes local name does not match any
// name in the baseline attribute allow list: Return drop.
if (!sBaselineAttributeAllowlist->Contains(aAttrLocalName)) {
return true;
}
// TODO(not specified yet): An element to attributes mapping that is
// considered before the attributes/removeAttributes lists that apply to
// everything.
if (mElements) {
int32_t namespaceID = aElement->NodeInfo()->NamespaceID();
RefPtr<nsAtom> nameAtom = aElement->NodeInfo()->NameAtom();
if (auto entry = mElements->Lookup(ElementName(namespaceID, nameAtom))) {
if (entry->mRemoveAttributes &&
MatchesAttributeName(*entry->mRemoveAttributes, aAttrNamespace,
aAttrLocalName)) {
return true;
}
if (entry->mAttributes) {
if (!MatchesAttributeName(*entry->mAttributes, aAttrNamespace,
aAttrLocalName)) {
return true;
}
// Fall-through to the removeAttributes/attributes lists below.
}
}
}
// Step 4. If attribute matches any attribute match list in configs attribute
// drop list: Return drop.
if (mRemoveAttributes &&
MatchesAttributeName(*mRemoveAttributes, aAttrNamespace,
aAttrLocalName)) {
return true;
}
// Step 5. If attribute allow list exists in config:
if (mAttributes) {
// Step 5.1. Then let allow list be |config|["allowAttributes"].
// Step 6. If attribute does not match any attribute match list in allow
// list: Return drop.
if (!MatchesAttributeName(*mAttributes, aAttrNamespace, aAttrLocalName)) {
return true;
}
} else {
// Step 5.2. Otherwise: Let allow list be the default configuration's
// attribute allow list.
// Step 6. If attribute does not match any attribute
// match list in allow list: Return drop.
if (!sDefaultConfigurationAttributeAllowlist->Contains(aAttrLocalName)) {
return true;
}
}
// Step 7. Return keep.
return false;
}
// https://wicg.github.io/sanitizer-api/#handle-funky-elements
bool nsTreeSanitizer::MustDropFunkyAttribute(Element* aElement,
int32_t aAttrNamespace,
nsAtom* aAttrLocalName) {
// Step 1. If elements element interface is HTMLTemplateElement:
// Note: This step is implemented in the main loop of SanitizeChildren.
// Step 2. If elements element interface has a HTMLHyperlinkElementUtils
// mixin, and if elements protocol property is "javascript:":
// TODO(https://github.com/WICG/sanitizer-api/issues/168)
if (aAttrLocalName == nsGkAtoms::href) {
if (nsCOMPtr<Link> link = do_QueryInterface(aElement)) {
nsCOMPtr<nsIURI> uri = link->GetURI();
if (uri && uri->SchemeIs("javascript")) {
// Step 2.1. Remove the `href` attribute from element.
return true;
}
}
}
// Step 3. if elements element interface is HTMLFormElement, and if elements
// action attribute is a URL with "javascript:" protocol:
if (auto* form = HTMLFormElement::FromNode(aElement)) {
if (aAttrNamespace == kNameSpaceID_None &&
aAttrLocalName == nsGkAtoms::action) {
nsCOMPtr<nsIURI> uri;
form->GetURIAttr(aAttrLocalName, nullptr, getter_AddRefs(uri));
if (uri && uri->SchemeIs("javascript")) {
// Step 3.1 Remove the `action` attribute from element.
return true;
}
}
}
// Step 4. if elements element interface is HTMLInputElement or
// HTMLButtonElement, and if elements formaction attribute is a [URL] with
// javascript: protocol
if (aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::button) &&
aAttrNamespace == kNameSpaceID_None &&
aAttrLocalName == nsGkAtoms::formaction) {
// XXX nsGenericHTMLFormControlElementWithState::GetFormAction falls back to
// the document URI.
nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(aElement);
nsCOMPtr<nsIURI> uri;
el->GetURIAttr(aAttrLocalName, nullptr, getter_AddRefs(uri));
if (uri && uri->SchemeIs("javascript")) {
// Step 4.1 Remove the `formaction` attribute from element.
return true;
}
}
return false;
}
bool nsTreeSanitizer::SanitizeURL(mozilla::dom::Element* aElement,
int32_t aNamespace, nsAtom* aLocalName,
bool aFragmentsOnly) {
nsAutoString value;
aElement->GetAttr(aNamespace, aLocalName, value);
// Get value and remove mandatory quotes
static const char* kWhitespace = "\n\r\t\b";
const nsAString& v = nsContentUtils::TrimCharsInSet(kWhitespace, value);
// Fragment-only url cannot be harmful.
if (!v.IsEmpty() && v.First() == u'#') {
return false;
}
// if we allow only same-document fragment URLs, stop and remove here
if (aFragmentsOnly) {
aElement->UnsetAttr(aNamespace, aLocalName, false);
if (mLogRemovals) {
LogMessage("Removed unsafe URI from element attribute.",
aElement->OwnerDoc(), aElement, aLocalName);
}
return true;
}
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
uint32_t flags = nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL;
nsCOMPtr<nsIURI> attrURI;
nsresult rv =
NS_NewURI(getter_AddRefs(attrURI), v, nullptr, aElement->GetBaseURI());
if (NS_SUCCEEDED(rv)) {
if (mCidEmbedsOnly && kNameSpaceID_None == aNamespace) {
if (nsGkAtoms::src == aLocalName || nsGkAtoms::background == aLocalName) {
// comm-central uses a hack that makes nsIURIs created with cid: specs
// actually have an about:blank spec. Therefore, nsIURI facilities are
// useless for cid: when comm-central code is participating.
if (!(v.Length() > 4 && (v[0] == 'c' || v[0] == 'C') &&
(v[1] == 'i' || v[1] == 'I') && (v[2] == 'd' || v[2] == 'D') &&
v[3] == ':')) {
rv = NS_ERROR_FAILURE;
}
} else if (nsGkAtoms::cdgroup_ == aLocalName ||
nsGkAtoms::altimg_ == aLocalName ||
nsGkAtoms::definitionURL_ == aLocalName) {
// Gecko doesn't fetch these now and shouldn't in the future, but
// in case someone goofs with these in the future, let's drop them.
rv = NS_ERROR_FAILURE;
} else {
rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags,
0);
}
} else {
rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags, 0);
}
}
if (NS_FAILED(rv)) {
aElement->UnsetAttr(aNamespace, aLocalName, false);
if (mLogRemovals) {
LogMessage("Removed unsafe URI from element attribute.",
aElement->OwnerDoc(), aElement, aLocalName);
}
return true;
}
return false;
}
void nsTreeSanitizer::Sanitize(DocumentFragment* aFragment) {
// If you want to relax these preconditions, be sure to check the code in
// here that notifies / does not notify or that fires mutation events if
// in tree.
MOZ_ASSERT(!aFragment->IsInUncomposedDoc(), "The fragment is in doc?");
mFullDocument = false;
SanitizeChildren(aFragment);
}
void nsTreeSanitizer::Sanitize(Document* aDocument) {
// If you want to relax these preconditions, be sure to check the code in
// here that notifies / does not notify or that fires mutation events if
// in tree.
#ifdef DEBUG
MOZ_ASSERT(!aDocument->GetContainer(), "The document is in a shell.");
RefPtr<mozilla::dom::Element> root = aDocument->GetRootElement();
MOZ_ASSERT(root->IsHTMLElement(nsGkAtoms::html), "Not HTML root.");
#endif
mFullDocument = true;
SanitizeChildren(aDocument);
}
void nsTreeSanitizer::SanitizeChildren(nsINode* aRoot) {
nsIContent* node = aRoot->GetFirstChild();
while (node) {
if (node->IsElement()) {
mozilla::dom::Element* elt = node->AsElement();
mozilla::dom::NodeInfo* nodeInfo = node->NodeInfo();
nsAtom* localName = nodeInfo->NameAtom();
int32_t ns = nodeInfo->NamespaceID();
if (MustPrune(ns, localName, elt)) {
if (mLogRemovals) {
LogMessage("Removing unsafe node.", elt->OwnerDoc(), elt);
}
RemoveAllAttributes(elt);
nsIContent* descendant = node;
while ((descendant = descendant->GetNextNode(node))) {
if (descendant->IsElement()) {
RemoveAllAttributes(descendant->AsElement());
}
}
nsIContent* next = node->GetNextNonChildNode(aRoot);
node->RemoveFromParent();
node = next;
continue;
}
if (auto* templateEl = HTMLTemplateElement::FromNode(elt)) {
// traverse into the DocFragment content attribute of template elements
bool wasFullDocument = mFullDocument;
mFullDocument = false;
RefPtr<DocumentFragment> frag = templateEl->Content();
SanitizeChildren(frag);
mFullDocument = wasFullDocument;
}
if (!mIsForSanitizerAPI && nsGkAtoms::style == localName) {
// If styles aren't allowed, style elements got pruned above. Even
// if styles are allowed, non-HTML, non-SVG style elements got pruned
// above.
NS_ASSERTION(ns == kNameSpaceID_XHTML || ns == kNameSpaceID_SVG,
"Should have only HTML or SVG here!");
if (SanitizeInlineStyle(elt, StyleSanitizationKind::Standard) &&
mLogRemovals) {
LogMessage("Removed some rules and/or properties from stylesheet.",
aRoot->OwnerDoc());
}
AllowedAttributes allowed;
allowed.mStyle = mAllowStyles;
if (ns == kNameSpaceID_XHTML) {
allowed.mNames = sAttributesHTML;
allowed.mURLs = kURLAttributesHTML;
} else {
allowed.mNames = sAttributesSVG;
allowed.mURLs = kURLAttributesSVG;
allowed.mXLink = true;
}
SanitizeAttributes(elt, allowed);
node = node->GetNextNonChildNode(aRoot);
continue;
}
if (MustFlatten(ns, localName)) {
if (mLogRemovals) {
LogMessage("Flattening unsafe node (descendants are preserved).",
elt->OwnerDoc(), elt);
}
RemoveAllAttributes(elt);
nsCOMPtr<nsIContent> next = node->GetNextNode(aRoot);
nsCOMPtr<nsIContent> parent = node->GetParent();
nsCOMPtr<nsIContent> child; // Must keep the child alive during move
ErrorResult rv;
while ((child = node->GetFirstChild())) {
nsCOMPtr<nsINode> refNode = node;
parent->InsertBefore(*child, refNode, rv);
if (rv.Failed()) {
break;
}
}
node->RemoveFromParent();
node = next;
continue;
}
NS_ASSERTION(ns == kNameSpaceID_XHTML || ns == kNameSpaceID_SVG ||
ns == kNameSpaceID_MathML,
"Should have only HTML, MathML or SVG here!");
AllowedAttributes allowed;
if (ns == kNameSpaceID_XHTML) {
allowed.mNames = sAttributesHTML;
allowed.mURLs = kURLAttributesHTML;
allowed.mStyle = mAllowStyles;
allowed.mDangerousSrc = nsGkAtoms::img == localName && !mCidEmbedsOnly;
SanitizeAttributes(elt, allowed);
} else if (ns == kNameSpaceID_SVG) {
allowed.mNames = sAttributesSVG;
allowed.mURLs = kURLAttributesSVG;
allowed.mXLink = true;
allowed.mStyle = mAllowStyles;
SanitizeAttributes(elt, allowed);
} else {
allowed.mNames = sAttributesMathML;
allowed.mURLs = kURLAttributesMathML;
allowed.mXLink = true;
SanitizeAttributes(elt, allowed);
}
node = node->GetNextNode(aRoot);
continue;
}
NS_ASSERTION(!node->GetFirstChild(), "How come non-element node had kids?");
nsIContent* next = node->GetNextNonChildNode(aRoot);
if (!mAllowComments && node->IsComment()) {
node->RemoveFromParent();
}
node = next;
}
}
void nsTreeSanitizer::RemoveAllAttributes(Element* aElement) {
const nsAttrName* attrName;
while ((attrName = aElement->GetAttrNameAt(0))) {
int32_t attrNs = attrName->NamespaceID();
RefPtr<nsAtom> attrLocal = attrName->LocalName();
aElement->UnsetAttr(attrNs, attrLocal, false);
}
}
void nsTreeSanitizer::RemoveAllAttributesFromDescendants(
mozilla::dom::Element* aElement) {
nsIContent* node = aElement->GetFirstChild();
while (node) {
if (node->IsElement()) {
mozilla::dom::Element* elt = node->AsElement();
RemoveAllAttributes(elt);
}
node = node->GetNextNode(aElement);
}
}
void nsTreeSanitizer::LogMessage(const char* aMessage, Document* aDoc,
Element* aElement, nsAtom* aAttr) {
if (mLogRemovals) {
nsAutoString msg;
msg.Assign(NS_ConvertASCIItoUTF16(aMessage));
if (aElement) {
msg.Append(u" Element: "_ns + aElement->LocalName() + u"."_ns);
}
if (aAttr) {
msg.Append(u" Attribute: "_ns + nsDependentAtomString(aAttr) + u"."_ns);
}
if (mInnerWindowID) {
nsContentUtils::ReportToConsoleByWindowID(
msg, nsIScriptError::warningFlag, "DOM"_ns, mInnerWindowID);
} else {
nsContentUtils::ReportToConsoleNonLocalized(
msg, nsIScriptError::warningFlag, "DOM"_ns, aDoc);
}
}
}
void nsTreeSanitizer::InitializeStatics() {
MOZ_ASSERT(!sElementsHTML, "Initializing a second time.");
sElementsHTML = new AtomsTable(ArrayLength(kElementsHTML));
for (uint32_t i = 0; kElementsHTML[i]; i++) {
sElementsHTML->Insert(kElementsHTML[i]);
}
sAttributesHTML = new AtomsTable(ArrayLength(kAttributesHTML));
for (uint32_t i = 0; kAttributesHTML[i]; i++) {
sAttributesHTML->Insert(kAttributesHTML[i]);
}
sPresAttributesHTML = new AtomsTable(ArrayLength(kPresAttributesHTML));
for (uint32_t i = 0; kPresAttributesHTML[i]; i++) {
sPresAttributesHTML->Insert(kPresAttributesHTML[i]);
}
sElementsSVG = new AtomsTable(ArrayLength(kElementsSVG));
for (uint32_t i = 0; kElementsSVG[i]; i++) {
sElementsSVG->Insert(kElementsSVG[i]);
}
sAttributesSVG = new AtomsTable(ArrayLength(kAttributesSVG));
for (uint32_t i = 0; kAttributesSVG[i]; i++) {
sAttributesSVG->Insert(kAttributesSVG[i]);
}
sElementsMathML = new AtomsTable(ArrayLength(kElementsMathML));
for (uint32_t i = 0; kElementsMathML[i]; i++) {
sElementsMathML->Insert(kElementsMathML[i]);
}
sAttributesMathML = new AtomsTable(ArrayLength(kAttributesMathML));
for (uint32_t i = 0; kAttributesMathML[i]; i++) {
sAttributesMathML->Insert(kAttributesMathML[i]);
}
sBaselineAttributeAllowlist =
new AtomsTable(ArrayLength(kBaselineAttributeAllowlist));
for (const auto* atom : kBaselineAttributeAllowlist) {
sBaselineAttributeAllowlist->Insert(atom);
}
sBaselineElementAllowlist =
new AtomsTable(ArrayLength(kBaselineElementAllowlist));
for (const auto* atom : kBaselineElementAllowlist) {
sBaselineElementAllowlist->Insert(atom);
}
sDefaultConfigurationAttributeAllowlist =
new AtomsTable(ArrayLength(kDefaultConfigurationAttributeAllowlist));
for (const auto* atom : kDefaultConfigurationAttributeAllowlist) {
sDefaultConfigurationAttributeAllowlist->Insert(atom);
}
sDefaultConfigurationElementAllowlist =
new AtomsTable(ArrayLength(kDefaultConfigurationElementAllowlist));
for (const auto* atom : kDefaultConfigurationElementAllowlist) {
sDefaultConfigurationElementAllowlist->Insert(atom);
}
nsCOMPtr<nsIPrincipal> principal =
NullPrincipal::CreateWithoutOriginAttributes();
principal.forget(&sNullPrincipal);
}
void nsTreeSanitizer::ReleaseStatics() {
delete sElementsHTML;
sElementsHTML = nullptr;
delete sAttributesHTML;
sAttributesHTML = nullptr;
delete sPresAttributesHTML;
sPresAttributesHTML = nullptr;
delete sElementsSVG;
sElementsSVG = nullptr;
delete sAttributesSVG;
sAttributesSVG = nullptr;
delete sElementsMathML;
sElementsMathML = nullptr;
delete sAttributesMathML;
sAttributesMathML = nullptr;
delete sBaselineAttributeAllowlist;
sBaselineAttributeAllowlist = nullptr;
delete sBaselineElementAllowlist;
sBaselineElementAllowlist = nullptr;
delete sDefaultConfigurationAttributeAllowlist;
sDefaultConfigurationAttributeAllowlist = nullptr;
delete sDefaultConfigurationElementAllowlist;
sDefaultConfigurationElementAllowlist = nullptr;
NS_IF_RELEASE(sNullPrincipal);
}
static int32_t ConvertNamespaceString(const nsAString& aNamespace,
bool aForAttribute,
mozilla::ErrorResult& aRv) {
if (aNamespace.IsVoid()) {
// NOTE: Currently this ?should? never match any elements, only
// attributes.
return kNameSpaceID_None;
}
int32_t namespaceID = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
aNamespace, /* aInChromeDoc */ false);
if (namespaceID == kNameSpaceID_XHTML || namespaceID == kNameSpaceID_MathML ||
namespaceID == kNameSpaceID_SVG) {
return namespaceID;
}
if (aForAttribute && (namespaceID == kNameSpaceID_XMLNS ||
namespaceID == kNameSpaceID_XLink)) {
return namespaceID;
}
aRv.ThrowTypeError("Invalid namespace: \""_ns +
NS_ConvertUTF16toUTF8(aNamespace) + "\"."_ns);
return kNameSpaceID_Unknown;
}
nsTreeSanitizer::ElementNameSet nsTreeSanitizer::ConvertElements(
const nsTArray<OwningStringOrSanitizerElementNamespace>& aElements,
mozilla::ErrorResult& aRv) {
ElementNameSet set(aElements.Length());
for (const auto& entry : aElements) {
if (entry.IsString()) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(entry.GetAsString());
// The default namespace for elements is HTML.
ElementName elemName(kNameSpaceID_XHTML, std::move(nameAtom));
set.Insert(elemName);
} else {
const auto& elemNamespace = entry.GetAsSanitizerElementNamespace();
int32_t namespaceID =
ConvertNamespaceString(elemNamespace.mNamespace, false, aRv);
if (aRv.Failed()) {
break;
}
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(elemNamespace.mName);
ElementName elemName(namespaceID, std::move(nameAtom));
set.Insert(elemName);
}
}
return set;
}
nsTreeSanitizer::ElementsToAttributesMap
nsTreeSanitizer::ConvertElementsWithAttributes(
const nsTArray<OwningStringOrSanitizerElementNamespaceWithAttributes>&
aElements,
mozilla::ErrorResult& aRv) {
ElementsToAttributesMap map;
for (const auto& entry : aElements) {
if (entry.IsString()) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(entry.GetAsString());
// The default namespace for elements is HTML.
ElementName elemName(kNameSpaceID_XHTML, std::move(nameAtom));
// No explicit list of attributes to allow/remove.
map.InsertOrUpdate(elemName, ElementWithAttributes{});
} else {
const auto& elemNamespace =
entry.GetAsSanitizerElementNamespaceWithAttributes();
ElementWithAttributes elemWithAttributes;
if (elemNamespace.mAttributes.WasPassed()) {
elemWithAttributes.mAttributes.emplace(
ConvertAttributes(elemNamespace.mAttributes.Value(), aRv));
if (aRv.Failed()) {
break;
}
}
if (elemNamespace.mRemoveAttributes.WasPassed()) {
elemWithAttributes.mRemoveAttributes.emplace(
ConvertAttributes(elemNamespace.mRemoveAttributes.Value(), aRv));
if (aRv.Failed()) {
break;
}
}
int32_t namespaceID =
ConvertNamespaceString(elemNamespace.mNamespace, false, aRv);
if (aRv.Failed()) {
break;
}
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(elemNamespace.mName);
ElementName elemName(namespaceID, std::move(nameAtom));
map.InsertOrUpdate(elemName, std::move(elemWithAttributes));
}
}
return map;
}
nsTreeSanitizer::AttributeNameSet nsTreeSanitizer::ConvertAttributes(
const nsTArray<OwningStringOrSanitizerAttributeNamespace>& aAttributes,
ErrorResult& aRv) {
AttributeNameSet set(aAttributes.Length());
for (const auto& entry : aAttributes) {
if (entry.IsString()) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(entry.GetAsString());
// The default namespace for attributes is the "null" namespace.
AttributeName attrName(kNameSpaceID_None, std::move(nameAtom));
set.Insert(attrName);
} else {
const auto& attrNamespace = entry.GetAsSanitizerAttributeNamespace();
int32_t namespaceID =
ConvertNamespaceString(attrNamespace.mNamespace, true, aRv);
if (aRv.Failed()) {
break;
}
RefPtr<nsAtom> attrAtom = NS_AtomizeMainThread(attrNamespace.mName);
AttributeName attrName(namespaceID, std::move(attrAtom));
set.Insert(attrName);
}
}
return set;
}
void nsTreeSanitizer::WithWebSanitizerOptions(
nsIGlobalObject* aGlobal, const mozilla::dom::SanitizerConfig& aOptions,
ErrorResult& aRv) {
if (StaticPrefs::dom_security_sanitizer_logging()) {
mLogRemovals = true;
if (nsPIDOMWindowInner* win = aGlobal->GetAsInnerWindow()) {
mInnerWindowID = win->WindowID();
}
}
mIsForSanitizerAPI = true;
if (aOptions.mComments.WasPassed()) {
mAllowComments = aOptions.mComments.Value();
}
if (aOptions.mCustomElements.WasPassed()) {
mAllowCustomElements = aOptions.mCustomElements.Value();
}
if (aOptions.mUnknownMarkup.WasPassed()) {
mAllowUnknownMarkup = aOptions.mUnknownMarkup.Value();
}
if (aOptions.mElements.WasPassed()) {
mElements.emplace(
ConvertElementsWithAttributes(aOptions.mElements.Value(), aRv));
if (aRv.Failed()) {
return;
}
}
if (aOptions.mRemoveElements.WasPassed()) {
mRemoveElements.emplace(
ConvertElements(aOptions.mRemoveElements.Value(), aRv));
if (aRv.Failed()) {
return;
}
}
if (aOptions.mReplaceWithChildrenElements.WasPassed()) {
mReplaceWithChildrenElements.emplace(
ConvertElements(aOptions.mReplaceWithChildrenElements.Value(), aRv));
if (aRv.Failed()) {
return;
}
}
if (aOptions.mAttributes.WasPassed()) {
mAttributes.emplace(ConvertAttributes(aOptions.mAttributes.Value(), aRv));
if (aRv.Failed()) {
return;
}
}
if (aOptions.mRemoveAttributes.WasPassed()) {
mRemoveAttributes.emplace(
ConvertAttributes(aOptions.mRemoveAttributes.Value(), aRv));
}
}