summaryrefslogtreecommitdiffstats
path: root/dom/base/nsTreeSanitizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsTreeSanitizer.cpp')
-rw-r--r--dom/base/nsTreeSanitizer.cpp1539
1 files changed, 1539 insertions, 0 deletions
diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp
new file mode 100644
index 0000000000..4818359592
--- /dev/null
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -0,0 +1,1539 @@
+/* -*- 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/BindingStyleRule.h"
+#include "mozilla/DeclarationBlock.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/css/Rule.h"
+#include "mozilla/dom/CSSRuleList.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/SRIMetadata.h"
+#include "mozilla/NullPrincipal.h"
+#include "nsCSSPropertyID.h"
+#include "nsUnicharInputStream.h"
+#include "nsAttrName.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.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::datalist,
+ nsGkAtoms::dd,
+ nsGkAtoms::del,
+ nsGkAtoms::details,
+ nsGkAtoms::dfn,
+ 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::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::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,
+ 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::mfenced_, // mfenced
+ 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
+};
+
+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;
+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),
+ mOnlyConditionalCSS(aFlags &
+ nsIParserUtils::SanitizerRemoveOnlyConditionalCSS) {
+ 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();
+ }
+ /* Ensure SanitizerRemoveOnlyConditionalCSS isn't combined with any
+ * flags, except SanitizerLogRemovals. */
+ MOZ_ASSERT(!mOnlyConditionalCSS ||
+ 0 ==
+ (aFlags & ~(nsIParserUtils::SanitizerRemoveOnlyConditionalCSS |
+ nsIParserUtils::SanitizerLogRemovals)));
+}
+
+bool nsTreeSanitizer::MustFlatten(int32_t aNamespace, nsAtom* 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;
+ }
+ 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::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) {
+ // 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(kNameSpaceID_None, nsGkAtoms::charset) ||
+ aElement->HasAttr(kNameSpaceID_None, 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(kNameSpaceID_None, nsGkAtoms::itemprop) ||
+ aElement->HasAttr(kNameSpaceID_None, 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) {
+ if (nsGkAtoms::style == aLocal &&
+ !(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG)) {
+ return true;
+ }
+ return false;
+ }
+ if (nsGkAtoms::style == aLocal) {
+ return true;
+ }
+ return false;
+}
+
+void nsTreeSanitizer::SanitizeStyleSheet(const nsAString& aOriginal,
+ nsAString& aSanitized,
+ Document* aDocument,
+ nsIURI* aBaseURI) {
+ aSanitized.Truncate();
+
+ NS_ConvertUTF16toUTF8 style(aOriginal);
+ RefPtr<nsIReferrerInfo> referrer =
+ ReferrerInfo::CreateForInternalCSSResources(aDocument);
+ auto extraData =
+ MakeRefPtr<URLExtraData>(aBaseURI, referrer, aDocument->NodePrincipal());
+ auto sanitizationKind = mOnlyConditionalCSS
+ ? StyleSanitizationKind::NoConditionalRules
+ : StyleSanitizationKind::Standard;
+ RefPtr<RawServoStyleSheetContents> contents =
+ Servo_StyleSheet_FromUTF8Bytes(
+ /* loader = */ nullptr,
+ /* stylesheet = */ nullptr,
+ /* load_data = */ nullptr, &style,
+ css::SheetParsingMode::eAuthorSheetFeatures, extraData.get(),
+ /* line_number_offset = */ 0, aDocument->GetCompatibilityMode(),
+ /* reusable_sheets = */ nullptr,
+ /* use_counters = */ nullptr, StyleAllowImportRules::Yes,
+ sanitizationKind, &aSanitized)
+ .Consume();
+
+ if (mLogRemovals && aSanitized.Length() != aOriginal.Length()) {
+ LogMessage("Removed some rules and/or properties from stylesheet.",
+ aDocument);
+ }
+}
+
+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) {
+ uint32_t ac = 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 (kNameSpaceID_None == attrNs) {
+ if (aAllowed.mStyle && nsGkAtoms::style == attrLocal) {
+ continue;
+ }
+ if (aAllowed.mDangerousSrc && nsGkAtoms::src == attrLocal) {
+ continue;
+ }
+ if (IsURL(aAllowed.mURLs, attrLocal)) {
+ if (SanitizeURL(aElement, attrNs, 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;
+ }
+ // 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) {
+ if (SanitizeURL(aElement, attrNs, 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 (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);
+ }
+}
+
+bool nsTreeSanitizer::SanitizeURL(mozilla::dom::Element* aElement,
+ int32_t aNamespace, nsAtom* aLocalName) {
+ 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;
+ }
+
+ 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 (!mOnlyConditionalCSS && 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 (nsGkAtoms::style == localName) {
+ // If !mOnlyConditionalCSS check the following condition:
+ // 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) ||
+ mOnlyConditionalCSS,
+ "Should have only HTML or SVG here!");
+ nsAutoString styleText;
+ nsContentUtils::GetNodeTextContent(node, false, styleText);
+
+ nsAutoString sanitizedStyle;
+ SanitizeStyleSheet(styleText, sanitizedStyle, aRoot->OwnerDoc(),
+ node->GetBaseURI());
+ RemoveAllAttributesFromDescendants(elt);
+ nsContentUtils::SetNodeTextContent(node, sanitizedStyle, true);
+
+ if (!mOnlyConditionalCSS) {
+ AllowedAttributes allowed;
+ allowed.mStyle = mAllowStyles;
+ if (ns == kNameSpaceID_XHTML) {
+ allowed.mNames = sAttributesHTML;
+ allowed.mURLs = kURLAttributesHTML;
+ SanitizeAttributes(elt, allowed);
+ } else {
+ allowed.mNames = sAttributesSVG;
+ allowed.mURLs = kURLAttributesSVG;
+ allowed.mXLink = true;
+ SanitizeAttributes(elt, allowed);
+ }
+ }
+ node = node->GetNextNonChildNode(aRoot);
+ continue;
+ }
+ if (!mOnlyConditionalCSS && 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;
+ }
+ if (!mOnlyConditionalCSS) {
+ 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 (!mOnlyConditionalCSS && (!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);
+ }
+
+ 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->PutEntry(kElementsHTML[i]);
+ }
+
+ sAttributesHTML = new AtomsTable(ArrayLength(kAttributesHTML));
+ for (uint32_t i = 0; kAttributesHTML[i]; i++) {
+ sAttributesHTML->PutEntry(kAttributesHTML[i]);
+ }
+
+ sPresAttributesHTML = new AtomsTable(ArrayLength(kPresAttributesHTML));
+ for (uint32_t i = 0; kPresAttributesHTML[i]; i++) {
+ sPresAttributesHTML->PutEntry(kPresAttributesHTML[i]);
+ }
+
+ sElementsSVG = new AtomsTable(ArrayLength(kElementsSVG));
+ for (uint32_t i = 0; kElementsSVG[i]; i++) {
+ sElementsSVG->PutEntry(kElementsSVG[i]);
+ }
+
+ sAttributesSVG = new AtomsTable(ArrayLength(kAttributesSVG));
+ for (uint32_t i = 0; kAttributesSVG[i]; i++) {
+ sAttributesSVG->PutEntry(kAttributesSVG[i]);
+ }
+
+ sElementsMathML = new AtomsTable(ArrayLength(kElementsMathML));
+ for (uint32_t i = 0; kElementsMathML[i]; i++) {
+ sElementsMathML->PutEntry(kElementsMathML[i]);
+ }
+
+ sAttributesMathML = new AtomsTable(ArrayLength(kAttributesMathML));
+ for (uint32_t i = 0; kAttributesMathML[i]; i++) {
+ sAttributesMathML->PutEntry(kAttributesMathML[i]);
+ }
+
+ 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;
+
+ NS_IF_RELEASE(sNullPrincipal);
+}