summaryrefslogtreecommitdiff
path: root/src/pkg/html
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
commitf154da9e12608589e8d5f0508f908a0c3e88a1bb (patch)
treef8255d51e10c6f1e0ed69702200b966c9556a431 /src/pkg/html
parent8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff)
downloadgolang-upstream/1.4.tar.gz
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/pkg/html')
-rw-r--r--src/pkg/html/entity.go2253
-rw-r--r--src/pkg/html/entity_test.go29
-rw-r--r--src/pkg/html/escape.go250
-rw-r--r--src/pkg/html/escape_test.go115
-rw-r--r--src/pkg/html/template/attr.go175
-rw-r--r--src/pkg/html/template/clone_test.go188
-rw-r--r--src/pkg/html/template/content.go136
-rw-r--r--src/pkg/html/template/content_test.go280
-rw-r--r--src/pkg/html/template/context.go339
-rw-r--r--src/pkg/html/template/css.go268
-rw-r--r--src/pkg/html/template/css_test.go281
-rw-r--r--src/pkg/html/template/doc.go191
-rw-r--r--src/pkg/html/template/error.go197
-rw-r--r--src/pkg/html/template/escape.go815
-rw-r--r--src/pkg/html/template/escape_test.go1692
-rw-r--r--src/pkg/html/template/html.go257
-rw-r--r--src/pkg/html/template/html_test.go94
-rw-r--r--src/pkg/html/template/js.go362
-rw-r--r--src/pkg/html/template/js_test.go401
-rw-r--r--src/pkg/html/template/template.go381
-rw-r--r--src/pkg/html/template/transition.go550
-rw-r--r--src/pkg/html/template/url.go105
-rw-r--r--src/pkg/html/template/url_test.go112
23 files changed, 0 insertions, 9471 deletions
diff --git a/src/pkg/html/entity.go b/src/pkg/html/entity.go
deleted file mode 100644
index af8a007ed..000000000
--- a/src/pkg/html/entity.go
+++ /dev/null
@@ -1,2253 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package html
-
-// All entities that do not end with ';' are 6 or fewer bytes long.
-const longestEntityWithoutSemicolon = 6
-
-// entity is a map from HTML entity names to their values. The semicolon matters:
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html
-// lists both "amp" and "amp;" as two separate entries.
-//
-// Note that the HTML5 list is larger than the HTML4 list at
-// http://www.w3.org/TR/html4/sgml/entities.html
-var entity = map[string]rune{
- "AElig;": '\U000000C6',
- "AMP;": '\U00000026',
- "Aacute;": '\U000000C1',
- "Abreve;": '\U00000102',
- "Acirc;": '\U000000C2',
- "Acy;": '\U00000410',
- "Afr;": '\U0001D504',
- "Agrave;": '\U000000C0',
- "Alpha;": '\U00000391',
- "Amacr;": '\U00000100',
- "And;": '\U00002A53',
- "Aogon;": '\U00000104',
- "Aopf;": '\U0001D538',
- "ApplyFunction;": '\U00002061',
- "Aring;": '\U000000C5',
- "Ascr;": '\U0001D49C',
- "Assign;": '\U00002254',
- "Atilde;": '\U000000C3',
- "Auml;": '\U000000C4',
- "Backslash;": '\U00002216',
- "Barv;": '\U00002AE7',
- "Barwed;": '\U00002306',
- "Bcy;": '\U00000411',
- "Because;": '\U00002235',
- "Bernoullis;": '\U0000212C',
- "Beta;": '\U00000392',
- "Bfr;": '\U0001D505',
- "Bopf;": '\U0001D539',
- "Breve;": '\U000002D8',
- "Bscr;": '\U0000212C',
- "Bumpeq;": '\U0000224E',
- "CHcy;": '\U00000427',
- "COPY;": '\U000000A9',
- "Cacute;": '\U00000106',
- "Cap;": '\U000022D2',
- "CapitalDifferentialD;": '\U00002145',
- "Cayleys;": '\U0000212D',
- "Ccaron;": '\U0000010C',
- "Ccedil;": '\U000000C7',
- "Ccirc;": '\U00000108',
- "Cconint;": '\U00002230',
- "Cdot;": '\U0000010A',
- "Cedilla;": '\U000000B8',
- "CenterDot;": '\U000000B7',
- "Cfr;": '\U0000212D',
- "Chi;": '\U000003A7',
- "CircleDot;": '\U00002299',
- "CircleMinus;": '\U00002296',
- "CirclePlus;": '\U00002295',
- "CircleTimes;": '\U00002297',
- "ClockwiseContourIntegral;": '\U00002232',
- "CloseCurlyDoubleQuote;": '\U0000201D',
- "CloseCurlyQuote;": '\U00002019',
- "Colon;": '\U00002237',
- "Colone;": '\U00002A74',
- "Congruent;": '\U00002261',
- "Conint;": '\U0000222F',
- "ContourIntegral;": '\U0000222E',
- "Copf;": '\U00002102',
- "Coproduct;": '\U00002210',
- "CounterClockwiseContourIntegral;": '\U00002233',
- "Cross;": '\U00002A2F',
- "Cscr;": '\U0001D49E',
- "Cup;": '\U000022D3',
- "CupCap;": '\U0000224D',
- "DD;": '\U00002145',
- "DDotrahd;": '\U00002911',
- "DJcy;": '\U00000402',
- "DScy;": '\U00000405',
- "DZcy;": '\U0000040F',
- "Dagger;": '\U00002021',
- "Darr;": '\U000021A1',
- "Dashv;": '\U00002AE4',
- "Dcaron;": '\U0000010E',
- "Dcy;": '\U00000414',
- "Del;": '\U00002207',
- "Delta;": '\U00000394',
- "Dfr;": '\U0001D507',
- "DiacriticalAcute;": '\U000000B4',
- "DiacriticalDot;": '\U000002D9',
- "DiacriticalDoubleAcute;": '\U000002DD',
- "DiacriticalGrave;": '\U00000060',
- "DiacriticalTilde;": '\U000002DC',
- "Diamond;": '\U000022C4',
- "DifferentialD;": '\U00002146',
- "Dopf;": '\U0001D53B',
- "Dot;": '\U000000A8',
- "DotDot;": '\U000020DC',
- "DotEqual;": '\U00002250',
- "DoubleContourIntegral;": '\U0000222F',
- "DoubleDot;": '\U000000A8',
- "DoubleDownArrow;": '\U000021D3',
- "DoubleLeftArrow;": '\U000021D0',
- "DoubleLeftRightArrow;": '\U000021D4',
- "DoubleLeftTee;": '\U00002AE4',
- "DoubleLongLeftArrow;": '\U000027F8',
- "DoubleLongLeftRightArrow;": '\U000027FA',
- "DoubleLongRightArrow;": '\U000027F9',
- "DoubleRightArrow;": '\U000021D2',
- "DoubleRightTee;": '\U000022A8',
- "DoubleUpArrow;": '\U000021D1',
- "DoubleUpDownArrow;": '\U000021D5',
- "DoubleVerticalBar;": '\U00002225',
- "DownArrow;": '\U00002193',
- "DownArrowBar;": '\U00002913',
- "DownArrowUpArrow;": '\U000021F5',
- "DownBreve;": '\U00000311',
- "DownLeftRightVector;": '\U00002950',
- "DownLeftTeeVector;": '\U0000295E',
- "DownLeftVector;": '\U000021BD',
- "DownLeftVectorBar;": '\U00002956',
- "DownRightTeeVector;": '\U0000295F',
- "DownRightVector;": '\U000021C1',
- "DownRightVectorBar;": '\U00002957',
- "DownTee;": '\U000022A4',
- "DownTeeArrow;": '\U000021A7',
- "Downarrow;": '\U000021D3',
- "Dscr;": '\U0001D49F',
- "Dstrok;": '\U00000110',
- "ENG;": '\U0000014A',
- "ETH;": '\U000000D0',
- "Eacute;": '\U000000C9',
- "Ecaron;": '\U0000011A',
- "Ecirc;": '\U000000CA',
- "Ecy;": '\U0000042D',
- "Edot;": '\U00000116',
- "Efr;": '\U0001D508',
- "Egrave;": '\U000000C8',
- "Element;": '\U00002208',
- "Emacr;": '\U00000112',
- "EmptySmallSquare;": '\U000025FB',
- "EmptyVerySmallSquare;": '\U000025AB',
- "Eogon;": '\U00000118',
- "Eopf;": '\U0001D53C',
- "Epsilon;": '\U00000395',
- "Equal;": '\U00002A75',
- "EqualTilde;": '\U00002242',
- "Equilibrium;": '\U000021CC',
- "Escr;": '\U00002130',
- "Esim;": '\U00002A73',
- "Eta;": '\U00000397',
- "Euml;": '\U000000CB',
- "Exists;": '\U00002203',
- "ExponentialE;": '\U00002147',
- "Fcy;": '\U00000424',
- "Ffr;": '\U0001D509',
- "FilledSmallSquare;": '\U000025FC',
- "FilledVerySmallSquare;": '\U000025AA',
- "Fopf;": '\U0001D53D',
- "ForAll;": '\U00002200',
- "Fouriertrf;": '\U00002131',
- "Fscr;": '\U00002131',
- "GJcy;": '\U00000403',
- "GT;": '\U0000003E',
- "Gamma;": '\U00000393',
- "Gammad;": '\U000003DC',
- "Gbreve;": '\U0000011E',
- "Gcedil;": '\U00000122',
- "Gcirc;": '\U0000011C',
- "Gcy;": '\U00000413',
- "Gdot;": '\U00000120',
- "Gfr;": '\U0001D50A',
- "Gg;": '\U000022D9',
- "Gopf;": '\U0001D53E',
- "GreaterEqual;": '\U00002265',
- "GreaterEqualLess;": '\U000022DB',
- "GreaterFullEqual;": '\U00002267',
- "GreaterGreater;": '\U00002AA2',
- "GreaterLess;": '\U00002277',
- "GreaterSlantEqual;": '\U00002A7E',
- "GreaterTilde;": '\U00002273',
- "Gscr;": '\U0001D4A2',
- "Gt;": '\U0000226B',
- "HARDcy;": '\U0000042A',
- "Hacek;": '\U000002C7',
- "Hat;": '\U0000005E',
- "Hcirc;": '\U00000124',
- "Hfr;": '\U0000210C',
- "HilbertSpace;": '\U0000210B',
- "Hopf;": '\U0000210D',
- "HorizontalLine;": '\U00002500',
- "Hscr;": '\U0000210B',
- "Hstrok;": '\U00000126',
- "HumpDownHump;": '\U0000224E',
- "HumpEqual;": '\U0000224F',
- "IEcy;": '\U00000415',
- "IJlig;": '\U00000132',
- "IOcy;": '\U00000401',
- "Iacute;": '\U000000CD',
- "Icirc;": '\U000000CE',
- "Icy;": '\U00000418',
- "Idot;": '\U00000130',
- "Ifr;": '\U00002111',
- "Igrave;": '\U000000CC',
- "Im;": '\U00002111',
- "Imacr;": '\U0000012A',
- "ImaginaryI;": '\U00002148',
- "Implies;": '\U000021D2',
- "Int;": '\U0000222C',
- "Integral;": '\U0000222B',
- "Intersection;": '\U000022C2',
- "InvisibleComma;": '\U00002063',
- "InvisibleTimes;": '\U00002062',
- "Iogon;": '\U0000012E',
- "Iopf;": '\U0001D540',
- "Iota;": '\U00000399',
- "Iscr;": '\U00002110',
- "Itilde;": '\U00000128',
- "Iukcy;": '\U00000406',
- "Iuml;": '\U000000CF',
- "Jcirc;": '\U00000134',
- "Jcy;": '\U00000419',
- "Jfr;": '\U0001D50D',
- "Jopf;": '\U0001D541',
- "Jscr;": '\U0001D4A5',
- "Jsercy;": '\U00000408',
- "Jukcy;": '\U00000404',
- "KHcy;": '\U00000425',
- "KJcy;": '\U0000040C',
- "Kappa;": '\U0000039A',
- "Kcedil;": '\U00000136',
- "Kcy;": '\U0000041A',
- "Kfr;": '\U0001D50E',
- "Kopf;": '\U0001D542',
- "Kscr;": '\U0001D4A6',
- "LJcy;": '\U00000409',
- "LT;": '\U0000003C',
- "Lacute;": '\U00000139',
- "Lambda;": '\U0000039B',
- "Lang;": '\U000027EA',
- "Laplacetrf;": '\U00002112',
- "Larr;": '\U0000219E',
- "Lcaron;": '\U0000013D',
- "Lcedil;": '\U0000013B',
- "Lcy;": '\U0000041B',
- "LeftAngleBracket;": '\U000027E8',
- "LeftArrow;": '\U00002190',
- "LeftArrowBar;": '\U000021E4',
- "LeftArrowRightArrow;": '\U000021C6',
- "LeftCeiling;": '\U00002308',
- "LeftDoubleBracket;": '\U000027E6',
- "LeftDownTeeVector;": '\U00002961',
- "LeftDownVector;": '\U000021C3',
- "LeftDownVectorBar;": '\U00002959',
- "LeftFloor;": '\U0000230A',
- "LeftRightArrow;": '\U00002194',
- "LeftRightVector;": '\U0000294E',
- "LeftTee;": '\U000022A3',
- "LeftTeeArrow;": '\U000021A4',
- "LeftTeeVector;": '\U0000295A',
- "LeftTriangle;": '\U000022B2',
- "LeftTriangleBar;": '\U000029CF',
- "LeftTriangleEqual;": '\U000022B4',
- "LeftUpDownVector;": '\U00002951',
- "LeftUpTeeVector;": '\U00002960',
- "LeftUpVector;": '\U000021BF',
- "LeftUpVectorBar;": '\U00002958',
- "LeftVector;": '\U000021BC',
- "LeftVectorBar;": '\U00002952',
- "Leftarrow;": '\U000021D0',
- "Leftrightarrow;": '\U000021D4',
- "LessEqualGreater;": '\U000022DA',
- "LessFullEqual;": '\U00002266',
- "LessGreater;": '\U00002276',
- "LessLess;": '\U00002AA1',
- "LessSlantEqual;": '\U00002A7D',
- "LessTilde;": '\U00002272',
- "Lfr;": '\U0001D50F',
- "Ll;": '\U000022D8',
- "Lleftarrow;": '\U000021DA',
- "Lmidot;": '\U0000013F',
- "LongLeftArrow;": '\U000027F5',
- "LongLeftRightArrow;": '\U000027F7',
- "LongRightArrow;": '\U000027F6',
- "Longleftarrow;": '\U000027F8',
- "Longleftrightarrow;": '\U000027FA',
- "Longrightarrow;": '\U000027F9',
- "Lopf;": '\U0001D543',
- "LowerLeftArrow;": '\U00002199',
- "LowerRightArrow;": '\U00002198',
- "Lscr;": '\U00002112',
- "Lsh;": '\U000021B0',
- "Lstrok;": '\U00000141',
- "Lt;": '\U0000226A',
- "Map;": '\U00002905',
- "Mcy;": '\U0000041C',
- "MediumSpace;": '\U0000205F',
- "Mellintrf;": '\U00002133',
- "Mfr;": '\U0001D510',
- "MinusPlus;": '\U00002213',
- "Mopf;": '\U0001D544',
- "Mscr;": '\U00002133',
- "Mu;": '\U0000039C',
- "NJcy;": '\U0000040A',
- "Nacute;": '\U00000143',
- "Ncaron;": '\U00000147',
- "Ncedil;": '\U00000145',
- "Ncy;": '\U0000041D',
- "NegativeMediumSpace;": '\U0000200B',
- "NegativeThickSpace;": '\U0000200B',
- "NegativeThinSpace;": '\U0000200B',
- "NegativeVeryThinSpace;": '\U0000200B',
- "NestedGreaterGreater;": '\U0000226B',
- "NestedLessLess;": '\U0000226A',
- "NewLine;": '\U0000000A',
- "Nfr;": '\U0001D511',
- "NoBreak;": '\U00002060',
- "NonBreakingSpace;": '\U000000A0',
- "Nopf;": '\U00002115',
- "Not;": '\U00002AEC',
- "NotCongruent;": '\U00002262',
- "NotCupCap;": '\U0000226D',
- "NotDoubleVerticalBar;": '\U00002226',
- "NotElement;": '\U00002209',
- "NotEqual;": '\U00002260',
- "NotExists;": '\U00002204',
- "NotGreater;": '\U0000226F',
- "NotGreaterEqual;": '\U00002271',
- "NotGreaterLess;": '\U00002279',
- "NotGreaterTilde;": '\U00002275',
- "NotLeftTriangle;": '\U000022EA',
- "NotLeftTriangleEqual;": '\U000022EC',
- "NotLess;": '\U0000226E',
- "NotLessEqual;": '\U00002270',
- "NotLessGreater;": '\U00002278',
- "NotLessTilde;": '\U00002274',
- "NotPrecedes;": '\U00002280',
- "NotPrecedesSlantEqual;": '\U000022E0',
- "NotReverseElement;": '\U0000220C',
- "NotRightTriangle;": '\U000022EB',
- "NotRightTriangleEqual;": '\U000022ED',
- "NotSquareSubsetEqual;": '\U000022E2',
- "NotSquareSupersetEqual;": '\U000022E3',
- "NotSubsetEqual;": '\U00002288',
- "NotSucceeds;": '\U00002281',
- "NotSucceedsSlantEqual;": '\U000022E1',
- "NotSupersetEqual;": '\U00002289',
- "NotTilde;": '\U00002241',
- "NotTildeEqual;": '\U00002244',
- "NotTildeFullEqual;": '\U00002247',
- "NotTildeTilde;": '\U00002249',
- "NotVerticalBar;": '\U00002224',
- "Nscr;": '\U0001D4A9',
- "Ntilde;": '\U000000D1',
- "Nu;": '\U0000039D',
- "OElig;": '\U00000152',
- "Oacute;": '\U000000D3',
- "Ocirc;": '\U000000D4',
- "Ocy;": '\U0000041E',
- "Odblac;": '\U00000150',
- "Ofr;": '\U0001D512',
- "Ograve;": '\U000000D2',
- "Omacr;": '\U0000014C',
- "Omega;": '\U000003A9',
- "Omicron;": '\U0000039F',
- "Oopf;": '\U0001D546',
- "OpenCurlyDoubleQuote;": '\U0000201C',
- "OpenCurlyQuote;": '\U00002018',
- "Or;": '\U00002A54',
- "Oscr;": '\U0001D4AA',
- "Oslash;": '\U000000D8',
- "Otilde;": '\U000000D5',
- "Otimes;": '\U00002A37',
- "Ouml;": '\U000000D6',
- "OverBar;": '\U0000203E',
- "OverBrace;": '\U000023DE',
- "OverBracket;": '\U000023B4',
- "OverParenthesis;": '\U000023DC',
- "PartialD;": '\U00002202',
- "Pcy;": '\U0000041F',
- "Pfr;": '\U0001D513',
- "Phi;": '\U000003A6',
- "Pi;": '\U000003A0',
- "PlusMinus;": '\U000000B1',
- "Poincareplane;": '\U0000210C',
- "Popf;": '\U00002119',
- "Pr;": '\U00002ABB',
- "Precedes;": '\U0000227A',
- "PrecedesEqual;": '\U00002AAF',
- "PrecedesSlantEqual;": '\U0000227C',
- "PrecedesTilde;": '\U0000227E',
- "Prime;": '\U00002033',
- "Product;": '\U0000220F',
- "Proportion;": '\U00002237',
- "Proportional;": '\U0000221D',
- "Pscr;": '\U0001D4AB',
- "Psi;": '\U000003A8',
- "QUOT;": '\U00000022',
- "Qfr;": '\U0001D514',
- "Qopf;": '\U0000211A',
- "Qscr;": '\U0001D4AC',
- "RBarr;": '\U00002910',
- "REG;": '\U000000AE',
- "Racute;": '\U00000154',
- "Rang;": '\U000027EB',
- "Rarr;": '\U000021A0',
- "Rarrtl;": '\U00002916',
- "Rcaron;": '\U00000158',
- "Rcedil;": '\U00000156',
- "Rcy;": '\U00000420',
- "Re;": '\U0000211C',
- "ReverseElement;": '\U0000220B',
- "ReverseEquilibrium;": '\U000021CB',
- "ReverseUpEquilibrium;": '\U0000296F',
- "Rfr;": '\U0000211C',
- "Rho;": '\U000003A1',
- "RightAngleBracket;": '\U000027E9',
- "RightArrow;": '\U00002192',
- "RightArrowBar;": '\U000021E5',
- "RightArrowLeftArrow;": '\U000021C4',
- "RightCeiling;": '\U00002309',
- "RightDoubleBracket;": '\U000027E7',
- "RightDownTeeVector;": '\U0000295D',
- "RightDownVector;": '\U000021C2',
- "RightDownVectorBar;": '\U00002955',
- "RightFloor;": '\U0000230B',
- "RightTee;": '\U000022A2',
- "RightTeeArrow;": '\U000021A6',
- "RightTeeVector;": '\U0000295B',
- "RightTriangle;": '\U000022B3',
- "RightTriangleBar;": '\U000029D0',
- "RightTriangleEqual;": '\U000022B5',
- "RightUpDownVector;": '\U0000294F',
- "RightUpTeeVector;": '\U0000295C',
- "RightUpVector;": '\U000021BE',
- "RightUpVectorBar;": '\U00002954',
- "RightVector;": '\U000021C0',
- "RightVectorBar;": '\U00002953',
- "Rightarrow;": '\U000021D2',
- "Ropf;": '\U0000211D',
- "RoundImplies;": '\U00002970',
- "Rrightarrow;": '\U000021DB',
- "Rscr;": '\U0000211B',
- "Rsh;": '\U000021B1',
- "RuleDelayed;": '\U000029F4',
- "SHCHcy;": '\U00000429',
- "SHcy;": '\U00000428',
- "SOFTcy;": '\U0000042C',
- "Sacute;": '\U0000015A',
- "Sc;": '\U00002ABC',
- "Scaron;": '\U00000160',
- "Scedil;": '\U0000015E',
- "Scirc;": '\U0000015C',
- "Scy;": '\U00000421',
- "Sfr;": '\U0001D516',
- "ShortDownArrow;": '\U00002193',
- "ShortLeftArrow;": '\U00002190',
- "ShortRightArrow;": '\U00002192',
- "ShortUpArrow;": '\U00002191',
- "Sigma;": '\U000003A3',
- "SmallCircle;": '\U00002218',
- "Sopf;": '\U0001D54A',
- "Sqrt;": '\U0000221A',
- "Square;": '\U000025A1',
- "SquareIntersection;": '\U00002293',
- "SquareSubset;": '\U0000228F',
- "SquareSubsetEqual;": '\U00002291',
- "SquareSuperset;": '\U00002290',
- "SquareSupersetEqual;": '\U00002292',
- "SquareUnion;": '\U00002294',
- "Sscr;": '\U0001D4AE',
- "Star;": '\U000022C6',
- "Sub;": '\U000022D0',
- "Subset;": '\U000022D0',
- "SubsetEqual;": '\U00002286',
- "Succeeds;": '\U0000227B',
- "SucceedsEqual;": '\U00002AB0',
- "SucceedsSlantEqual;": '\U0000227D',
- "SucceedsTilde;": '\U0000227F',
- "SuchThat;": '\U0000220B',
- "Sum;": '\U00002211',
- "Sup;": '\U000022D1',
- "Superset;": '\U00002283',
- "SupersetEqual;": '\U00002287',
- "Supset;": '\U000022D1',
- "THORN;": '\U000000DE',
- "TRADE;": '\U00002122',
- "TSHcy;": '\U0000040B',
- "TScy;": '\U00000426',
- "Tab;": '\U00000009',
- "Tau;": '\U000003A4',
- "Tcaron;": '\U00000164',
- "Tcedil;": '\U00000162',
- "Tcy;": '\U00000422',
- "Tfr;": '\U0001D517',
- "Therefore;": '\U00002234',
- "Theta;": '\U00000398',
- "ThinSpace;": '\U00002009',
- "Tilde;": '\U0000223C',
- "TildeEqual;": '\U00002243',
- "TildeFullEqual;": '\U00002245',
- "TildeTilde;": '\U00002248',
- "Topf;": '\U0001D54B',
- "TripleDot;": '\U000020DB',
- "Tscr;": '\U0001D4AF',
- "Tstrok;": '\U00000166',
- "Uacute;": '\U000000DA',
- "Uarr;": '\U0000219F',
- "Uarrocir;": '\U00002949',
- "Ubrcy;": '\U0000040E',
- "Ubreve;": '\U0000016C',
- "Ucirc;": '\U000000DB',
- "Ucy;": '\U00000423',
- "Udblac;": '\U00000170',
- "Ufr;": '\U0001D518',
- "Ugrave;": '\U000000D9',
- "Umacr;": '\U0000016A',
- "UnderBar;": '\U0000005F',
- "UnderBrace;": '\U000023DF',
- "UnderBracket;": '\U000023B5',
- "UnderParenthesis;": '\U000023DD',
- "Union;": '\U000022C3',
- "UnionPlus;": '\U0000228E',
- "Uogon;": '\U00000172',
- "Uopf;": '\U0001D54C',
- "UpArrow;": '\U00002191',
- "UpArrowBar;": '\U00002912',
- "UpArrowDownArrow;": '\U000021C5',
- "UpDownArrow;": '\U00002195',
- "UpEquilibrium;": '\U0000296E',
- "UpTee;": '\U000022A5',
- "UpTeeArrow;": '\U000021A5',
- "Uparrow;": '\U000021D1',
- "Updownarrow;": '\U000021D5',
- "UpperLeftArrow;": '\U00002196',
- "UpperRightArrow;": '\U00002197',
- "Upsi;": '\U000003D2',
- "Upsilon;": '\U000003A5',
- "Uring;": '\U0000016E',
- "Uscr;": '\U0001D4B0',
- "Utilde;": '\U00000168',
- "Uuml;": '\U000000DC',
- "VDash;": '\U000022AB',
- "Vbar;": '\U00002AEB',
- "Vcy;": '\U00000412',
- "Vdash;": '\U000022A9',
- "Vdashl;": '\U00002AE6',
- "Vee;": '\U000022C1',
- "Verbar;": '\U00002016',
- "Vert;": '\U00002016',
- "VerticalBar;": '\U00002223',
- "VerticalLine;": '\U0000007C',
- "VerticalSeparator;": '\U00002758',
- "VerticalTilde;": '\U00002240',
- "VeryThinSpace;": '\U0000200A',
- "Vfr;": '\U0001D519',
- "Vopf;": '\U0001D54D',
- "Vscr;": '\U0001D4B1',
- "Vvdash;": '\U000022AA',
- "Wcirc;": '\U00000174',
- "Wedge;": '\U000022C0',
- "Wfr;": '\U0001D51A',
- "Wopf;": '\U0001D54E',
- "Wscr;": '\U0001D4B2',
- "Xfr;": '\U0001D51B',
- "Xi;": '\U0000039E',
- "Xopf;": '\U0001D54F',
- "Xscr;": '\U0001D4B3',
- "YAcy;": '\U0000042F',
- "YIcy;": '\U00000407',
- "YUcy;": '\U0000042E',
- "Yacute;": '\U000000DD',
- "Ycirc;": '\U00000176',
- "Ycy;": '\U0000042B',
- "Yfr;": '\U0001D51C',
- "Yopf;": '\U0001D550',
- "Yscr;": '\U0001D4B4',
- "Yuml;": '\U00000178',
- "ZHcy;": '\U00000416',
- "Zacute;": '\U00000179',
- "Zcaron;": '\U0000017D',
- "Zcy;": '\U00000417',
- "Zdot;": '\U0000017B',
- "ZeroWidthSpace;": '\U0000200B',
- "Zeta;": '\U00000396',
- "Zfr;": '\U00002128',
- "Zopf;": '\U00002124',
- "Zscr;": '\U0001D4B5',
- "aacute;": '\U000000E1',
- "abreve;": '\U00000103',
- "ac;": '\U0000223E',
- "acd;": '\U0000223F',
- "acirc;": '\U000000E2',
- "acute;": '\U000000B4',
- "acy;": '\U00000430',
- "aelig;": '\U000000E6',
- "af;": '\U00002061',
- "afr;": '\U0001D51E',
- "agrave;": '\U000000E0',
- "alefsym;": '\U00002135',
- "aleph;": '\U00002135',
- "alpha;": '\U000003B1',
- "amacr;": '\U00000101',
- "amalg;": '\U00002A3F',
- "amp;": '\U00000026',
- "and;": '\U00002227',
- "andand;": '\U00002A55',
- "andd;": '\U00002A5C',
- "andslope;": '\U00002A58',
- "andv;": '\U00002A5A',
- "ang;": '\U00002220',
- "ange;": '\U000029A4',
- "angle;": '\U00002220',
- "angmsd;": '\U00002221',
- "angmsdaa;": '\U000029A8',
- "angmsdab;": '\U000029A9',
- "angmsdac;": '\U000029AA',
- "angmsdad;": '\U000029AB',
- "angmsdae;": '\U000029AC',
- "angmsdaf;": '\U000029AD',
- "angmsdag;": '\U000029AE',
- "angmsdah;": '\U000029AF',
- "angrt;": '\U0000221F',
- "angrtvb;": '\U000022BE',
- "angrtvbd;": '\U0000299D',
- "angsph;": '\U00002222',
- "angst;": '\U000000C5',
- "angzarr;": '\U0000237C',
- "aogon;": '\U00000105',
- "aopf;": '\U0001D552',
- "ap;": '\U00002248',
- "apE;": '\U00002A70',
- "apacir;": '\U00002A6F',
- "ape;": '\U0000224A',
- "apid;": '\U0000224B',
- "apos;": '\U00000027',
- "approx;": '\U00002248',
- "approxeq;": '\U0000224A',
- "aring;": '\U000000E5',
- "ascr;": '\U0001D4B6',
- "ast;": '\U0000002A',
- "asymp;": '\U00002248',
- "asympeq;": '\U0000224D',
- "atilde;": '\U000000E3',
- "auml;": '\U000000E4',
- "awconint;": '\U00002233',
- "awint;": '\U00002A11',
- "bNot;": '\U00002AED',
- "backcong;": '\U0000224C',
- "backepsilon;": '\U000003F6',
- "backprime;": '\U00002035',
- "backsim;": '\U0000223D',
- "backsimeq;": '\U000022CD',
- "barvee;": '\U000022BD',
- "barwed;": '\U00002305',
- "barwedge;": '\U00002305',
- "bbrk;": '\U000023B5',
- "bbrktbrk;": '\U000023B6',
- "bcong;": '\U0000224C',
- "bcy;": '\U00000431',
- "bdquo;": '\U0000201E',
- "becaus;": '\U00002235',
- "because;": '\U00002235',
- "bemptyv;": '\U000029B0',
- "bepsi;": '\U000003F6',
- "bernou;": '\U0000212C',
- "beta;": '\U000003B2',
- "beth;": '\U00002136',
- "between;": '\U0000226C',
- "bfr;": '\U0001D51F',
- "bigcap;": '\U000022C2',
- "bigcirc;": '\U000025EF',
- "bigcup;": '\U000022C3',
- "bigodot;": '\U00002A00',
- "bigoplus;": '\U00002A01',
- "bigotimes;": '\U00002A02',
- "bigsqcup;": '\U00002A06',
- "bigstar;": '\U00002605',
- "bigtriangledown;": '\U000025BD',
- "bigtriangleup;": '\U000025B3',
- "biguplus;": '\U00002A04',
- "bigvee;": '\U000022C1',
- "bigwedge;": '\U000022C0',
- "bkarow;": '\U0000290D',
- "blacklozenge;": '\U000029EB',
- "blacksquare;": '\U000025AA',
- "blacktriangle;": '\U000025B4',
- "blacktriangledown;": '\U000025BE',
- "blacktriangleleft;": '\U000025C2',
- "blacktriangleright;": '\U000025B8',
- "blank;": '\U00002423',
- "blk12;": '\U00002592',
- "blk14;": '\U00002591',
- "blk34;": '\U00002593',
- "block;": '\U00002588',
- "bnot;": '\U00002310',
- "bopf;": '\U0001D553',
- "bot;": '\U000022A5',
- "bottom;": '\U000022A5',
- "bowtie;": '\U000022C8',
- "boxDL;": '\U00002557',
- "boxDR;": '\U00002554',
- "boxDl;": '\U00002556',
- "boxDr;": '\U00002553',
- "boxH;": '\U00002550',
- "boxHD;": '\U00002566',
- "boxHU;": '\U00002569',
- "boxHd;": '\U00002564',
- "boxHu;": '\U00002567',
- "boxUL;": '\U0000255D',
- "boxUR;": '\U0000255A',
- "boxUl;": '\U0000255C',
- "boxUr;": '\U00002559',
- "boxV;": '\U00002551',
- "boxVH;": '\U0000256C',
- "boxVL;": '\U00002563',
- "boxVR;": '\U00002560',
- "boxVh;": '\U0000256B',
- "boxVl;": '\U00002562',
- "boxVr;": '\U0000255F',
- "boxbox;": '\U000029C9',
- "boxdL;": '\U00002555',
- "boxdR;": '\U00002552',
- "boxdl;": '\U00002510',
- "boxdr;": '\U0000250C',
- "boxh;": '\U00002500',
- "boxhD;": '\U00002565',
- "boxhU;": '\U00002568',
- "boxhd;": '\U0000252C',
- "boxhu;": '\U00002534',
- "boxminus;": '\U0000229F',
- "boxplus;": '\U0000229E',
- "boxtimes;": '\U000022A0',
- "boxuL;": '\U0000255B',
- "boxuR;": '\U00002558',
- "boxul;": '\U00002518',
- "boxur;": '\U00002514',
- "boxv;": '\U00002502',
- "boxvH;": '\U0000256A',
- "boxvL;": '\U00002561',
- "boxvR;": '\U0000255E',
- "boxvh;": '\U0000253C',
- "boxvl;": '\U00002524',
- "boxvr;": '\U0000251C',
- "bprime;": '\U00002035',
- "breve;": '\U000002D8',
- "brvbar;": '\U000000A6',
- "bscr;": '\U0001D4B7',
- "bsemi;": '\U0000204F',
- "bsim;": '\U0000223D',
- "bsime;": '\U000022CD',
- "bsol;": '\U0000005C',
- "bsolb;": '\U000029C5',
- "bsolhsub;": '\U000027C8',
- "bull;": '\U00002022',
- "bullet;": '\U00002022',
- "bump;": '\U0000224E',
- "bumpE;": '\U00002AAE',
- "bumpe;": '\U0000224F',
- "bumpeq;": '\U0000224F',
- "cacute;": '\U00000107',
- "cap;": '\U00002229',
- "capand;": '\U00002A44',
- "capbrcup;": '\U00002A49',
- "capcap;": '\U00002A4B',
- "capcup;": '\U00002A47',
- "capdot;": '\U00002A40',
- "caret;": '\U00002041',
- "caron;": '\U000002C7',
- "ccaps;": '\U00002A4D',
- "ccaron;": '\U0000010D',
- "ccedil;": '\U000000E7',
- "ccirc;": '\U00000109',
- "ccups;": '\U00002A4C',
- "ccupssm;": '\U00002A50',
- "cdot;": '\U0000010B',
- "cedil;": '\U000000B8',
- "cemptyv;": '\U000029B2',
- "cent;": '\U000000A2',
- "centerdot;": '\U000000B7',
- "cfr;": '\U0001D520',
- "chcy;": '\U00000447',
- "check;": '\U00002713',
- "checkmark;": '\U00002713',
- "chi;": '\U000003C7',
- "cir;": '\U000025CB',
- "cirE;": '\U000029C3',
- "circ;": '\U000002C6',
- "circeq;": '\U00002257',
- "circlearrowleft;": '\U000021BA',
- "circlearrowright;": '\U000021BB',
- "circledR;": '\U000000AE',
- "circledS;": '\U000024C8',
- "circledast;": '\U0000229B',
- "circledcirc;": '\U0000229A',
- "circleddash;": '\U0000229D',
- "cire;": '\U00002257',
- "cirfnint;": '\U00002A10',
- "cirmid;": '\U00002AEF',
- "cirscir;": '\U000029C2',
- "clubs;": '\U00002663',
- "clubsuit;": '\U00002663',
- "colon;": '\U0000003A',
- "colone;": '\U00002254',
- "coloneq;": '\U00002254',
- "comma;": '\U0000002C',
- "commat;": '\U00000040',
- "comp;": '\U00002201',
- "compfn;": '\U00002218',
- "complement;": '\U00002201',
- "complexes;": '\U00002102',
- "cong;": '\U00002245',
- "congdot;": '\U00002A6D',
- "conint;": '\U0000222E',
- "copf;": '\U0001D554',
- "coprod;": '\U00002210',
- "copy;": '\U000000A9',
- "copysr;": '\U00002117',
- "crarr;": '\U000021B5',
- "cross;": '\U00002717',
- "cscr;": '\U0001D4B8',
- "csub;": '\U00002ACF',
- "csube;": '\U00002AD1',
- "csup;": '\U00002AD0',
- "csupe;": '\U00002AD2',
- "ctdot;": '\U000022EF',
- "cudarrl;": '\U00002938',
- "cudarrr;": '\U00002935',
- "cuepr;": '\U000022DE',
- "cuesc;": '\U000022DF',
- "cularr;": '\U000021B6',
- "cularrp;": '\U0000293D',
- "cup;": '\U0000222A',
- "cupbrcap;": '\U00002A48',
- "cupcap;": '\U00002A46',
- "cupcup;": '\U00002A4A',
- "cupdot;": '\U0000228D',
- "cupor;": '\U00002A45',
- "curarr;": '\U000021B7',
- "curarrm;": '\U0000293C',
- "curlyeqprec;": '\U000022DE',
- "curlyeqsucc;": '\U000022DF',
- "curlyvee;": '\U000022CE',
- "curlywedge;": '\U000022CF',
- "curren;": '\U000000A4',
- "curvearrowleft;": '\U000021B6',
- "curvearrowright;": '\U000021B7',
- "cuvee;": '\U000022CE',
- "cuwed;": '\U000022CF',
- "cwconint;": '\U00002232',
- "cwint;": '\U00002231',
- "cylcty;": '\U0000232D',
- "dArr;": '\U000021D3',
- "dHar;": '\U00002965',
- "dagger;": '\U00002020',
- "daleth;": '\U00002138',
- "darr;": '\U00002193',
- "dash;": '\U00002010',
- "dashv;": '\U000022A3',
- "dbkarow;": '\U0000290F',
- "dblac;": '\U000002DD',
- "dcaron;": '\U0000010F',
- "dcy;": '\U00000434',
- "dd;": '\U00002146',
- "ddagger;": '\U00002021',
- "ddarr;": '\U000021CA',
- "ddotseq;": '\U00002A77',
- "deg;": '\U000000B0',
- "delta;": '\U000003B4',
- "demptyv;": '\U000029B1',
- "dfisht;": '\U0000297F',
- "dfr;": '\U0001D521',
- "dharl;": '\U000021C3',
- "dharr;": '\U000021C2',
- "diam;": '\U000022C4',
- "diamond;": '\U000022C4',
- "diamondsuit;": '\U00002666',
- "diams;": '\U00002666',
- "die;": '\U000000A8',
- "digamma;": '\U000003DD',
- "disin;": '\U000022F2',
- "div;": '\U000000F7',
- "divide;": '\U000000F7',
- "divideontimes;": '\U000022C7',
- "divonx;": '\U000022C7',
- "djcy;": '\U00000452',
- "dlcorn;": '\U0000231E',
- "dlcrop;": '\U0000230D',
- "dollar;": '\U00000024',
- "dopf;": '\U0001D555',
- "dot;": '\U000002D9',
- "doteq;": '\U00002250',
- "doteqdot;": '\U00002251',
- "dotminus;": '\U00002238',
- "dotplus;": '\U00002214',
- "dotsquare;": '\U000022A1',
- "doublebarwedge;": '\U00002306',
- "downarrow;": '\U00002193',
- "downdownarrows;": '\U000021CA',
- "downharpoonleft;": '\U000021C3',
- "downharpoonright;": '\U000021C2',
- "drbkarow;": '\U00002910',
- "drcorn;": '\U0000231F',
- "drcrop;": '\U0000230C',
- "dscr;": '\U0001D4B9',
- "dscy;": '\U00000455',
- "dsol;": '\U000029F6',
- "dstrok;": '\U00000111',
- "dtdot;": '\U000022F1',
- "dtri;": '\U000025BF',
- "dtrif;": '\U000025BE',
- "duarr;": '\U000021F5',
- "duhar;": '\U0000296F',
- "dwangle;": '\U000029A6',
- "dzcy;": '\U0000045F',
- "dzigrarr;": '\U000027FF',
- "eDDot;": '\U00002A77',
- "eDot;": '\U00002251',
- "eacute;": '\U000000E9',
- "easter;": '\U00002A6E',
- "ecaron;": '\U0000011B',
- "ecir;": '\U00002256',
- "ecirc;": '\U000000EA',
- "ecolon;": '\U00002255',
- "ecy;": '\U0000044D',
- "edot;": '\U00000117',
- "ee;": '\U00002147',
- "efDot;": '\U00002252',
- "efr;": '\U0001D522',
- "eg;": '\U00002A9A',
- "egrave;": '\U000000E8',
- "egs;": '\U00002A96',
- "egsdot;": '\U00002A98',
- "el;": '\U00002A99',
- "elinters;": '\U000023E7',
- "ell;": '\U00002113',
- "els;": '\U00002A95',
- "elsdot;": '\U00002A97',
- "emacr;": '\U00000113',
- "empty;": '\U00002205',
- "emptyset;": '\U00002205',
- "emptyv;": '\U00002205',
- "emsp;": '\U00002003',
- "emsp13;": '\U00002004',
- "emsp14;": '\U00002005',
- "eng;": '\U0000014B',
- "ensp;": '\U00002002',
- "eogon;": '\U00000119',
- "eopf;": '\U0001D556',
- "epar;": '\U000022D5',
- "eparsl;": '\U000029E3',
- "eplus;": '\U00002A71',
- "epsi;": '\U000003B5',
- "epsilon;": '\U000003B5',
- "epsiv;": '\U000003F5',
- "eqcirc;": '\U00002256',
- "eqcolon;": '\U00002255',
- "eqsim;": '\U00002242',
- "eqslantgtr;": '\U00002A96',
- "eqslantless;": '\U00002A95',
- "equals;": '\U0000003D',
- "equest;": '\U0000225F',
- "equiv;": '\U00002261',
- "equivDD;": '\U00002A78',
- "eqvparsl;": '\U000029E5',
- "erDot;": '\U00002253',
- "erarr;": '\U00002971',
- "escr;": '\U0000212F',
- "esdot;": '\U00002250',
- "esim;": '\U00002242',
- "eta;": '\U000003B7',
- "eth;": '\U000000F0',
- "euml;": '\U000000EB',
- "euro;": '\U000020AC',
- "excl;": '\U00000021',
- "exist;": '\U00002203',
- "expectation;": '\U00002130',
- "exponentiale;": '\U00002147',
- "fallingdotseq;": '\U00002252',
- "fcy;": '\U00000444',
- "female;": '\U00002640',
- "ffilig;": '\U0000FB03',
- "fflig;": '\U0000FB00',
- "ffllig;": '\U0000FB04',
- "ffr;": '\U0001D523',
- "filig;": '\U0000FB01',
- "flat;": '\U0000266D',
- "fllig;": '\U0000FB02',
- "fltns;": '\U000025B1',
- "fnof;": '\U00000192',
- "fopf;": '\U0001D557',
- "forall;": '\U00002200',
- "fork;": '\U000022D4',
- "forkv;": '\U00002AD9',
- "fpartint;": '\U00002A0D',
- "frac12;": '\U000000BD',
- "frac13;": '\U00002153',
- "frac14;": '\U000000BC',
- "frac15;": '\U00002155',
- "frac16;": '\U00002159',
- "frac18;": '\U0000215B',
- "frac23;": '\U00002154',
- "frac25;": '\U00002156',
- "frac34;": '\U000000BE',
- "frac35;": '\U00002157',
- "frac38;": '\U0000215C',
- "frac45;": '\U00002158',
- "frac56;": '\U0000215A',
- "frac58;": '\U0000215D',
- "frac78;": '\U0000215E',
- "frasl;": '\U00002044',
- "frown;": '\U00002322',
- "fscr;": '\U0001D4BB',
- "gE;": '\U00002267',
- "gEl;": '\U00002A8C',
- "gacute;": '\U000001F5',
- "gamma;": '\U000003B3',
- "gammad;": '\U000003DD',
- "gap;": '\U00002A86',
- "gbreve;": '\U0000011F',
- "gcirc;": '\U0000011D',
- "gcy;": '\U00000433',
- "gdot;": '\U00000121',
- "ge;": '\U00002265',
- "gel;": '\U000022DB',
- "geq;": '\U00002265',
- "geqq;": '\U00002267',
- "geqslant;": '\U00002A7E',
- "ges;": '\U00002A7E',
- "gescc;": '\U00002AA9',
- "gesdot;": '\U00002A80',
- "gesdoto;": '\U00002A82',
- "gesdotol;": '\U00002A84',
- "gesles;": '\U00002A94',
- "gfr;": '\U0001D524',
- "gg;": '\U0000226B',
- "ggg;": '\U000022D9',
- "gimel;": '\U00002137',
- "gjcy;": '\U00000453',
- "gl;": '\U00002277',
- "glE;": '\U00002A92',
- "gla;": '\U00002AA5',
- "glj;": '\U00002AA4',
- "gnE;": '\U00002269',
- "gnap;": '\U00002A8A',
- "gnapprox;": '\U00002A8A',
- "gne;": '\U00002A88',
- "gneq;": '\U00002A88',
- "gneqq;": '\U00002269',
- "gnsim;": '\U000022E7',
- "gopf;": '\U0001D558',
- "grave;": '\U00000060',
- "gscr;": '\U0000210A',
- "gsim;": '\U00002273',
- "gsime;": '\U00002A8E',
- "gsiml;": '\U00002A90',
- "gt;": '\U0000003E',
- "gtcc;": '\U00002AA7',
- "gtcir;": '\U00002A7A',
- "gtdot;": '\U000022D7',
- "gtlPar;": '\U00002995',
- "gtquest;": '\U00002A7C',
- "gtrapprox;": '\U00002A86',
- "gtrarr;": '\U00002978',
- "gtrdot;": '\U000022D7',
- "gtreqless;": '\U000022DB',
- "gtreqqless;": '\U00002A8C',
- "gtrless;": '\U00002277',
- "gtrsim;": '\U00002273',
- "hArr;": '\U000021D4',
- "hairsp;": '\U0000200A',
- "half;": '\U000000BD',
- "hamilt;": '\U0000210B',
- "hardcy;": '\U0000044A',
- "harr;": '\U00002194',
- "harrcir;": '\U00002948',
- "harrw;": '\U000021AD',
- "hbar;": '\U0000210F',
- "hcirc;": '\U00000125',
- "hearts;": '\U00002665',
- "heartsuit;": '\U00002665',
- "hellip;": '\U00002026',
- "hercon;": '\U000022B9',
- "hfr;": '\U0001D525',
- "hksearow;": '\U00002925',
- "hkswarow;": '\U00002926',
- "hoarr;": '\U000021FF',
- "homtht;": '\U0000223B',
- "hookleftarrow;": '\U000021A9',
- "hookrightarrow;": '\U000021AA',
- "hopf;": '\U0001D559',
- "horbar;": '\U00002015',
- "hscr;": '\U0001D4BD',
- "hslash;": '\U0000210F',
- "hstrok;": '\U00000127',
- "hybull;": '\U00002043',
- "hyphen;": '\U00002010',
- "iacute;": '\U000000ED',
- "ic;": '\U00002063',
- "icirc;": '\U000000EE',
- "icy;": '\U00000438',
- "iecy;": '\U00000435',
- "iexcl;": '\U000000A1',
- "iff;": '\U000021D4',
- "ifr;": '\U0001D526',
- "igrave;": '\U000000EC',
- "ii;": '\U00002148',
- "iiiint;": '\U00002A0C',
- "iiint;": '\U0000222D',
- "iinfin;": '\U000029DC',
- "iiota;": '\U00002129',
- "ijlig;": '\U00000133',
- "imacr;": '\U0000012B',
- "image;": '\U00002111',
- "imagline;": '\U00002110',
- "imagpart;": '\U00002111',
- "imath;": '\U00000131',
- "imof;": '\U000022B7',
- "imped;": '\U000001B5',
- "in;": '\U00002208',
- "incare;": '\U00002105',
- "infin;": '\U0000221E',
- "infintie;": '\U000029DD',
- "inodot;": '\U00000131',
- "int;": '\U0000222B',
- "intcal;": '\U000022BA',
- "integers;": '\U00002124',
- "intercal;": '\U000022BA',
- "intlarhk;": '\U00002A17',
- "intprod;": '\U00002A3C',
- "iocy;": '\U00000451',
- "iogon;": '\U0000012F',
- "iopf;": '\U0001D55A',
- "iota;": '\U000003B9',
- "iprod;": '\U00002A3C',
- "iquest;": '\U000000BF',
- "iscr;": '\U0001D4BE',
- "isin;": '\U00002208',
- "isinE;": '\U000022F9',
- "isindot;": '\U000022F5',
- "isins;": '\U000022F4',
- "isinsv;": '\U000022F3',
- "isinv;": '\U00002208',
- "it;": '\U00002062',
- "itilde;": '\U00000129',
- "iukcy;": '\U00000456',
- "iuml;": '\U000000EF',
- "jcirc;": '\U00000135',
- "jcy;": '\U00000439',
- "jfr;": '\U0001D527',
- "jmath;": '\U00000237',
- "jopf;": '\U0001D55B',
- "jscr;": '\U0001D4BF',
- "jsercy;": '\U00000458',
- "jukcy;": '\U00000454',
- "kappa;": '\U000003BA',
- "kappav;": '\U000003F0',
- "kcedil;": '\U00000137',
- "kcy;": '\U0000043A',
- "kfr;": '\U0001D528',
- "kgreen;": '\U00000138',
- "khcy;": '\U00000445',
- "kjcy;": '\U0000045C',
- "kopf;": '\U0001D55C',
- "kscr;": '\U0001D4C0',
- "lAarr;": '\U000021DA',
- "lArr;": '\U000021D0',
- "lAtail;": '\U0000291B',
- "lBarr;": '\U0000290E',
- "lE;": '\U00002266',
- "lEg;": '\U00002A8B',
- "lHar;": '\U00002962',
- "lacute;": '\U0000013A',
- "laemptyv;": '\U000029B4',
- "lagran;": '\U00002112',
- "lambda;": '\U000003BB',
- "lang;": '\U000027E8',
- "langd;": '\U00002991',
- "langle;": '\U000027E8',
- "lap;": '\U00002A85',
- "laquo;": '\U000000AB',
- "larr;": '\U00002190',
- "larrb;": '\U000021E4',
- "larrbfs;": '\U0000291F',
- "larrfs;": '\U0000291D',
- "larrhk;": '\U000021A9',
- "larrlp;": '\U000021AB',
- "larrpl;": '\U00002939',
- "larrsim;": '\U00002973',
- "larrtl;": '\U000021A2',
- "lat;": '\U00002AAB',
- "latail;": '\U00002919',
- "late;": '\U00002AAD',
- "lbarr;": '\U0000290C',
- "lbbrk;": '\U00002772',
- "lbrace;": '\U0000007B',
- "lbrack;": '\U0000005B',
- "lbrke;": '\U0000298B',
- "lbrksld;": '\U0000298F',
- "lbrkslu;": '\U0000298D',
- "lcaron;": '\U0000013E',
- "lcedil;": '\U0000013C',
- "lceil;": '\U00002308',
- "lcub;": '\U0000007B',
- "lcy;": '\U0000043B',
- "ldca;": '\U00002936',
- "ldquo;": '\U0000201C',
- "ldquor;": '\U0000201E',
- "ldrdhar;": '\U00002967',
- "ldrushar;": '\U0000294B',
- "ldsh;": '\U000021B2',
- "le;": '\U00002264',
- "leftarrow;": '\U00002190',
- "leftarrowtail;": '\U000021A2',
- "leftharpoondown;": '\U000021BD',
- "leftharpoonup;": '\U000021BC',
- "leftleftarrows;": '\U000021C7',
- "leftrightarrow;": '\U00002194',
- "leftrightarrows;": '\U000021C6',
- "leftrightharpoons;": '\U000021CB',
- "leftrightsquigarrow;": '\U000021AD',
- "leftthreetimes;": '\U000022CB',
- "leg;": '\U000022DA',
- "leq;": '\U00002264',
- "leqq;": '\U00002266',
- "leqslant;": '\U00002A7D',
- "les;": '\U00002A7D',
- "lescc;": '\U00002AA8',
- "lesdot;": '\U00002A7F',
- "lesdoto;": '\U00002A81',
- "lesdotor;": '\U00002A83',
- "lesges;": '\U00002A93',
- "lessapprox;": '\U00002A85',
- "lessdot;": '\U000022D6',
- "lesseqgtr;": '\U000022DA',
- "lesseqqgtr;": '\U00002A8B',
- "lessgtr;": '\U00002276',
- "lesssim;": '\U00002272',
- "lfisht;": '\U0000297C',
- "lfloor;": '\U0000230A',
- "lfr;": '\U0001D529',
- "lg;": '\U00002276',
- "lgE;": '\U00002A91',
- "lhard;": '\U000021BD',
- "lharu;": '\U000021BC',
- "lharul;": '\U0000296A',
- "lhblk;": '\U00002584',
- "ljcy;": '\U00000459',
- "ll;": '\U0000226A',
- "llarr;": '\U000021C7',
- "llcorner;": '\U0000231E',
- "llhard;": '\U0000296B',
- "lltri;": '\U000025FA',
- "lmidot;": '\U00000140',
- "lmoust;": '\U000023B0',
- "lmoustache;": '\U000023B0',
- "lnE;": '\U00002268',
- "lnap;": '\U00002A89',
- "lnapprox;": '\U00002A89',
- "lne;": '\U00002A87',
- "lneq;": '\U00002A87',
- "lneqq;": '\U00002268',
- "lnsim;": '\U000022E6',
- "loang;": '\U000027EC',
- "loarr;": '\U000021FD',
- "lobrk;": '\U000027E6',
- "longleftarrow;": '\U000027F5',
- "longleftrightarrow;": '\U000027F7',
- "longmapsto;": '\U000027FC',
- "longrightarrow;": '\U000027F6',
- "looparrowleft;": '\U000021AB',
- "looparrowright;": '\U000021AC',
- "lopar;": '\U00002985',
- "lopf;": '\U0001D55D',
- "loplus;": '\U00002A2D',
- "lotimes;": '\U00002A34',
- "lowast;": '\U00002217',
- "lowbar;": '\U0000005F',
- "loz;": '\U000025CA',
- "lozenge;": '\U000025CA',
- "lozf;": '\U000029EB',
- "lpar;": '\U00000028',
- "lparlt;": '\U00002993',
- "lrarr;": '\U000021C6',
- "lrcorner;": '\U0000231F',
- "lrhar;": '\U000021CB',
- "lrhard;": '\U0000296D',
- "lrm;": '\U0000200E',
- "lrtri;": '\U000022BF',
- "lsaquo;": '\U00002039',
- "lscr;": '\U0001D4C1',
- "lsh;": '\U000021B0',
- "lsim;": '\U00002272',
- "lsime;": '\U00002A8D',
- "lsimg;": '\U00002A8F',
- "lsqb;": '\U0000005B',
- "lsquo;": '\U00002018',
- "lsquor;": '\U0000201A',
- "lstrok;": '\U00000142',
- "lt;": '\U0000003C',
- "ltcc;": '\U00002AA6',
- "ltcir;": '\U00002A79',
- "ltdot;": '\U000022D6',
- "lthree;": '\U000022CB',
- "ltimes;": '\U000022C9',
- "ltlarr;": '\U00002976',
- "ltquest;": '\U00002A7B',
- "ltrPar;": '\U00002996',
- "ltri;": '\U000025C3',
- "ltrie;": '\U000022B4',
- "ltrif;": '\U000025C2',
- "lurdshar;": '\U0000294A',
- "luruhar;": '\U00002966',
- "mDDot;": '\U0000223A',
- "macr;": '\U000000AF',
- "male;": '\U00002642',
- "malt;": '\U00002720',
- "maltese;": '\U00002720',
- "map;": '\U000021A6',
- "mapsto;": '\U000021A6',
- "mapstodown;": '\U000021A7',
- "mapstoleft;": '\U000021A4',
- "mapstoup;": '\U000021A5',
- "marker;": '\U000025AE',
- "mcomma;": '\U00002A29',
- "mcy;": '\U0000043C',
- "mdash;": '\U00002014',
- "measuredangle;": '\U00002221',
- "mfr;": '\U0001D52A',
- "mho;": '\U00002127',
- "micro;": '\U000000B5',
- "mid;": '\U00002223',
- "midast;": '\U0000002A',
- "midcir;": '\U00002AF0',
- "middot;": '\U000000B7',
- "minus;": '\U00002212',
- "minusb;": '\U0000229F',
- "minusd;": '\U00002238',
- "minusdu;": '\U00002A2A',
- "mlcp;": '\U00002ADB',
- "mldr;": '\U00002026',
- "mnplus;": '\U00002213',
- "models;": '\U000022A7',
- "mopf;": '\U0001D55E',
- "mp;": '\U00002213',
- "mscr;": '\U0001D4C2',
- "mstpos;": '\U0000223E',
- "mu;": '\U000003BC',
- "multimap;": '\U000022B8',
- "mumap;": '\U000022B8',
- "nLeftarrow;": '\U000021CD',
- "nLeftrightarrow;": '\U000021CE',
- "nRightarrow;": '\U000021CF',
- "nVDash;": '\U000022AF',
- "nVdash;": '\U000022AE',
- "nabla;": '\U00002207',
- "nacute;": '\U00000144',
- "nap;": '\U00002249',
- "napos;": '\U00000149',
- "napprox;": '\U00002249',
- "natur;": '\U0000266E',
- "natural;": '\U0000266E',
- "naturals;": '\U00002115',
- "nbsp;": '\U000000A0',
- "ncap;": '\U00002A43',
- "ncaron;": '\U00000148',
- "ncedil;": '\U00000146',
- "ncong;": '\U00002247',
- "ncup;": '\U00002A42',
- "ncy;": '\U0000043D',
- "ndash;": '\U00002013',
- "ne;": '\U00002260',
- "neArr;": '\U000021D7',
- "nearhk;": '\U00002924',
- "nearr;": '\U00002197',
- "nearrow;": '\U00002197',
- "nequiv;": '\U00002262',
- "nesear;": '\U00002928',
- "nexist;": '\U00002204',
- "nexists;": '\U00002204',
- "nfr;": '\U0001D52B',
- "nge;": '\U00002271',
- "ngeq;": '\U00002271',
- "ngsim;": '\U00002275',
- "ngt;": '\U0000226F',
- "ngtr;": '\U0000226F',
- "nhArr;": '\U000021CE',
- "nharr;": '\U000021AE',
- "nhpar;": '\U00002AF2',
- "ni;": '\U0000220B',
- "nis;": '\U000022FC',
- "nisd;": '\U000022FA',
- "niv;": '\U0000220B',
- "njcy;": '\U0000045A',
- "nlArr;": '\U000021CD',
- "nlarr;": '\U0000219A',
- "nldr;": '\U00002025',
- "nle;": '\U00002270',
- "nleftarrow;": '\U0000219A',
- "nleftrightarrow;": '\U000021AE',
- "nleq;": '\U00002270',
- "nless;": '\U0000226E',
- "nlsim;": '\U00002274',
- "nlt;": '\U0000226E',
- "nltri;": '\U000022EA',
- "nltrie;": '\U000022EC',
- "nmid;": '\U00002224',
- "nopf;": '\U0001D55F',
- "not;": '\U000000AC',
- "notin;": '\U00002209',
- "notinva;": '\U00002209',
- "notinvb;": '\U000022F7',
- "notinvc;": '\U000022F6',
- "notni;": '\U0000220C',
- "notniva;": '\U0000220C',
- "notnivb;": '\U000022FE',
- "notnivc;": '\U000022FD',
- "npar;": '\U00002226',
- "nparallel;": '\U00002226',
- "npolint;": '\U00002A14',
- "npr;": '\U00002280',
- "nprcue;": '\U000022E0',
- "nprec;": '\U00002280',
- "nrArr;": '\U000021CF',
- "nrarr;": '\U0000219B',
- "nrightarrow;": '\U0000219B',
- "nrtri;": '\U000022EB',
- "nrtrie;": '\U000022ED',
- "nsc;": '\U00002281',
- "nsccue;": '\U000022E1',
- "nscr;": '\U0001D4C3',
- "nshortmid;": '\U00002224',
- "nshortparallel;": '\U00002226',
- "nsim;": '\U00002241',
- "nsime;": '\U00002244',
- "nsimeq;": '\U00002244',
- "nsmid;": '\U00002224',
- "nspar;": '\U00002226',
- "nsqsube;": '\U000022E2',
- "nsqsupe;": '\U000022E3',
- "nsub;": '\U00002284',
- "nsube;": '\U00002288',
- "nsubseteq;": '\U00002288',
- "nsucc;": '\U00002281',
- "nsup;": '\U00002285',
- "nsupe;": '\U00002289',
- "nsupseteq;": '\U00002289',
- "ntgl;": '\U00002279',
- "ntilde;": '\U000000F1',
- "ntlg;": '\U00002278',
- "ntriangleleft;": '\U000022EA',
- "ntrianglelefteq;": '\U000022EC',
- "ntriangleright;": '\U000022EB',
- "ntrianglerighteq;": '\U000022ED',
- "nu;": '\U000003BD',
- "num;": '\U00000023',
- "numero;": '\U00002116',
- "numsp;": '\U00002007',
- "nvDash;": '\U000022AD',
- "nvHarr;": '\U00002904',
- "nvdash;": '\U000022AC',
- "nvinfin;": '\U000029DE',
- "nvlArr;": '\U00002902',
- "nvrArr;": '\U00002903',
- "nwArr;": '\U000021D6',
- "nwarhk;": '\U00002923',
- "nwarr;": '\U00002196',
- "nwarrow;": '\U00002196',
- "nwnear;": '\U00002927',
- "oS;": '\U000024C8',
- "oacute;": '\U000000F3',
- "oast;": '\U0000229B',
- "ocir;": '\U0000229A',
- "ocirc;": '\U000000F4',
- "ocy;": '\U0000043E',
- "odash;": '\U0000229D',
- "odblac;": '\U00000151',
- "odiv;": '\U00002A38',
- "odot;": '\U00002299',
- "odsold;": '\U000029BC',
- "oelig;": '\U00000153',
- "ofcir;": '\U000029BF',
- "ofr;": '\U0001D52C',
- "ogon;": '\U000002DB',
- "ograve;": '\U000000F2',
- "ogt;": '\U000029C1',
- "ohbar;": '\U000029B5',
- "ohm;": '\U000003A9',
- "oint;": '\U0000222E',
- "olarr;": '\U000021BA',
- "olcir;": '\U000029BE',
- "olcross;": '\U000029BB',
- "oline;": '\U0000203E',
- "olt;": '\U000029C0',
- "omacr;": '\U0000014D',
- "omega;": '\U000003C9',
- "omicron;": '\U000003BF',
- "omid;": '\U000029B6',
- "ominus;": '\U00002296',
- "oopf;": '\U0001D560',
- "opar;": '\U000029B7',
- "operp;": '\U000029B9',
- "oplus;": '\U00002295',
- "or;": '\U00002228',
- "orarr;": '\U000021BB',
- "ord;": '\U00002A5D',
- "order;": '\U00002134',
- "orderof;": '\U00002134',
- "ordf;": '\U000000AA',
- "ordm;": '\U000000BA',
- "origof;": '\U000022B6',
- "oror;": '\U00002A56',
- "orslope;": '\U00002A57',
- "orv;": '\U00002A5B',
- "oscr;": '\U00002134',
- "oslash;": '\U000000F8',
- "osol;": '\U00002298',
- "otilde;": '\U000000F5',
- "otimes;": '\U00002297',
- "otimesas;": '\U00002A36',
- "ouml;": '\U000000F6',
- "ovbar;": '\U0000233D',
- "par;": '\U00002225',
- "para;": '\U000000B6',
- "parallel;": '\U00002225',
- "parsim;": '\U00002AF3',
- "parsl;": '\U00002AFD',
- "part;": '\U00002202',
- "pcy;": '\U0000043F',
- "percnt;": '\U00000025',
- "period;": '\U0000002E',
- "permil;": '\U00002030',
- "perp;": '\U000022A5',
- "pertenk;": '\U00002031',
- "pfr;": '\U0001D52D',
- "phi;": '\U000003C6',
- "phiv;": '\U000003D5',
- "phmmat;": '\U00002133',
- "phone;": '\U0000260E',
- "pi;": '\U000003C0',
- "pitchfork;": '\U000022D4',
- "piv;": '\U000003D6',
- "planck;": '\U0000210F',
- "planckh;": '\U0000210E',
- "plankv;": '\U0000210F',
- "plus;": '\U0000002B',
- "plusacir;": '\U00002A23',
- "plusb;": '\U0000229E',
- "pluscir;": '\U00002A22',
- "plusdo;": '\U00002214',
- "plusdu;": '\U00002A25',
- "pluse;": '\U00002A72',
- "plusmn;": '\U000000B1',
- "plussim;": '\U00002A26',
- "plustwo;": '\U00002A27',
- "pm;": '\U000000B1',
- "pointint;": '\U00002A15',
- "popf;": '\U0001D561',
- "pound;": '\U000000A3',
- "pr;": '\U0000227A',
- "prE;": '\U00002AB3',
- "prap;": '\U00002AB7',
- "prcue;": '\U0000227C',
- "pre;": '\U00002AAF',
- "prec;": '\U0000227A',
- "precapprox;": '\U00002AB7',
- "preccurlyeq;": '\U0000227C',
- "preceq;": '\U00002AAF',
- "precnapprox;": '\U00002AB9',
- "precneqq;": '\U00002AB5',
- "precnsim;": '\U000022E8',
- "precsim;": '\U0000227E',
- "prime;": '\U00002032',
- "primes;": '\U00002119',
- "prnE;": '\U00002AB5',
- "prnap;": '\U00002AB9',
- "prnsim;": '\U000022E8',
- "prod;": '\U0000220F',
- "profalar;": '\U0000232E',
- "profline;": '\U00002312',
- "profsurf;": '\U00002313',
- "prop;": '\U0000221D',
- "propto;": '\U0000221D',
- "prsim;": '\U0000227E',
- "prurel;": '\U000022B0',
- "pscr;": '\U0001D4C5',
- "psi;": '\U000003C8',
- "puncsp;": '\U00002008',
- "qfr;": '\U0001D52E',
- "qint;": '\U00002A0C',
- "qopf;": '\U0001D562',
- "qprime;": '\U00002057',
- "qscr;": '\U0001D4C6',
- "quaternions;": '\U0000210D',
- "quatint;": '\U00002A16',
- "quest;": '\U0000003F',
- "questeq;": '\U0000225F',
- "quot;": '\U00000022',
- "rAarr;": '\U000021DB',
- "rArr;": '\U000021D2',
- "rAtail;": '\U0000291C',
- "rBarr;": '\U0000290F',
- "rHar;": '\U00002964',
- "racute;": '\U00000155',
- "radic;": '\U0000221A',
- "raemptyv;": '\U000029B3',
- "rang;": '\U000027E9',
- "rangd;": '\U00002992',
- "range;": '\U000029A5',
- "rangle;": '\U000027E9',
- "raquo;": '\U000000BB',
- "rarr;": '\U00002192',
- "rarrap;": '\U00002975',
- "rarrb;": '\U000021E5',
- "rarrbfs;": '\U00002920',
- "rarrc;": '\U00002933',
- "rarrfs;": '\U0000291E',
- "rarrhk;": '\U000021AA',
- "rarrlp;": '\U000021AC',
- "rarrpl;": '\U00002945',
- "rarrsim;": '\U00002974',
- "rarrtl;": '\U000021A3',
- "rarrw;": '\U0000219D',
- "ratail;": '\U0000291A',
- "ratio;": '\U00002236',
- "rationals;": '\U0000211A',
- "rbarr;": '\U0000290D',
- "rbbrk;": '\U00002773',
- "rbrace;": '\U0000007D',
- "rbrack;": '\U0000005D',
- "rbrke;": '\U0000298C',
- "rbrksld;": '\U0000298E',
- "rbrkslu;": '\U00002990',
- "rcaron;": '\U00000159',
- "rcedil;": '\U00000157',
- "rceil;": '\U00002309',
- "rcub;": '\U0000007D',
- "rcy;": '\U00000440',
- "rdca;": '\U00002937',
- "rdldhar;": '\U00002969',
- "rdquo;": '\U0000201D',
- "rdquor;": '\U0000201D',
- "rdsh;": '\U000021B3',
- "real;": '\U0000211C',
- "realine;": '\U0000211B',
- "realpart;": '\U0000211C',
- "reals;": '\U0000211D',
- "rect;": '\U000025AD',
- "reg;": '\U000000AE',
- "rfisht;": '\U0000297D',
- "rfloor;": '\U0000230B',
- "rfr;": '\U0001D52F',
- "rhard;": '\U000021C1',
- "rharu;": '\U000021C0',
- "rharul;": '\U0000296C',
- "rho;": '\U000003C1',
- "rhov;": '\U000003F1',
- "rightarrow;": '\U00002192',
- "rightarrowtail;": '\U000021A3',
- "rightharpoondown;": '\U000021C1',
- "rightharpoonup;": '\U000021C0',
- "rightleftarrows;": '\U000021C4',
- "rightleftharpoons;": '\U000021CC',
- "rightrightarrows;": '\U000021C9',
- "rightsquigarrow;": '\U0000219D',
- "rightthreetimes;": '\U000022CC',
- "ring;": '\U000002DA',
- "risingdotseq;": '\U00002253',
- "rlarr;": '\U000021C4',
- "rlhar;": '\U000021CC',
- "rlm;": '\U0000200F',
- "rmoust;": '\U000023B1',
- "rmoustache;": '\U000023B1',
- "rnmid;": '\U00002AEE',
- "roang;": '\U000027ED',
- "roarr;": '\U000021FE',
- "robrk;": '\U000027E7',
- "ropar;": '\U00002986',
- "ropf;": '\U0001D563',
- "roplus;": '\U00002A2E',
- "rotimes;": '\U00002A35',
- "rpar;": '\U00000029',
- "rpargt;": '\U00002994',
- "rppolint;": '\U00002A12',
- "rrarr;": '\U000021C9',
- "rsaquo;": '\U0000203A',
- "rscr;": '\U0001D4C7',
- "rsh;": '\U000021B1',
- "rsqb;": '\U0000005D',
- "rsquo;": '\U00002019',
- "rsquor;": '\U00002019',
- "rthree;": '\U000022CC',
- "rtimes;": '\U000022CA',
- "rtri;": '\U000025B9',
- "rtrie;": '\U000022B5',
- "rtrif;": '\U000025B8',
- "rtriltri;": '\U000029CE',
- "ruluhar;": '\U00002968',
- "rx;": '\U0000211E',
- "sacute;": '\U0000015B',
- "sbquo;": '\U0000201A',
- "sc;": '\U0000227B',
- "scE;": '\U00002AB4',
- "scap;": '\U00002AB8',
- "scaron;": '\U00000161',
- "sccue;": '\U0000227D',
- "sce;": '\U00002AB0',
- "scedil;": '\U0000015F',
- "scirc;": '\U0000015D',
- "scnE;": '\U00002AB6',
- "scnap;": '\U00002ABA',
- "scnsim;": '\U000022E9',
- "scpolint;": '\U00002A13',
- "scsim;": '\U0000227F',
- "scy;": '\U00000441',
- "sdot;": '\U000022C5',
- "sdotb;": '\U000022A1',
- "sdote;": '\U00002A66',
- "seArr;": '\U000021D8',
- "searhk;": '\U00002925',
- "searr;": '\U00002198',
- "searrow;": '\U00002198',
- "sect;": '\U000000A7',
- "semi;": '\U0000003B',
- "seswar;": '\U00002929',
- "setminus;": '\U00002216',
- "setmn;": '\U00002216',
- "sext;": '\U00002736',
- "sfr;": '\U0001D530',
- "sfrown;": '\U00002322',
- "sharp;": '\U0000266F',
- "shchcy;": '\U00000449',
- "shcy;": '\U00000448',
- "shortmid;": '\U00002223',
- "shortparallel;": '\U00002225',
- "shy;": '\U000000AD',
- "sigma;": '\U000003C3',
- "sigmaf;": '\U000003C2',
- "sigmav;": '\U000003C2',
- "sim;": '\U0000223C',
- "simdot;": '\U00002A6A',
- "sime;": '\U00002243',
- "simeq;": '\U00002243',
- "simg;": '\U00002A9E',
- "simgE;": '\U00002AA0',
- "siml;": '\U00002A9D',
- "simlE;": '\U00002A9F',
- "simne;": '\U00002246',
- "simplus;": '\U00002A24',
- "simrarr;": '\U00002972',
- "slarr;": '\U00002190',
- "smallsetminus;": '\U00002216',
- "smashp;": '\U00002A33',
- "smeparsl;": '\U000029E4',
- "smid;": '\U00002223',
- "smile;": '\U00002323',
- "smt;": '\U00002AAA',
- "smte;": '\U00002AAC',
- "softcy;": '\U0000044C',
- "sol;": '\U0000002F',
- "solb;": '\U000029C4',
- "solbar;": '\U0000233F',
- "sopf;": '\U0001D564',
- "spades;": '\U00002660',
- "spadesuit;": '\U00002660',
- "spar;": '\U00002225',
- "sqcap;": '\U00002293',
- "sqcup;": '\U00002294',
- "sqsub;": '\U0000228F',
- "sqsube;": '\U00002291',
- "sqsubset;": '\U0000228F',
- "sqsubseteq;": '\U00002291',
- "sqsup;": '\U00002290',
- "sqsupe;": '\U00002292',
- "sqsupset;": '\U00002290',
- "sqsupseteq;": '\U00002292',
- "squ;": '\U000025A1',
- "square;": '\U000025A1',
- "squarf;": '\U000025AA',
- "squf;": '\U000025AA',
- "srarr;": '\U00002192',
- "sscr;": '\U0001D4C8',
- "ssetmn;": '\U00002216',
- "ssmile;": '\U00002323',
- "sstarf;": '\U000022C6',
- "star;": '\U00002606',
- "starf;": '\U00002605',
- "straightepsilon;": '\U000003F5',
- "straightphi;": '\U000003D5',
- "strns;": '\U000000AF',
- "sub;": '\U00002282',
- "subE;": '\U00002AC5',
- "subdot;": '\U00002ABD',
- "sube;": '\U00002286',
- "subedot;": '\U00002AC3',
- "submult;": '\U00002AC1',
- "subnE;": '\U00002ACB',
- "subne;": '\U0000228A',
- "subplus;": '\U00002ABF',
- "subrarr;": '\U00002979',
- "subset;": '\U00002282',
- "subseteq;": '\U00002286',
- "subseteqq;": '\U00002AC5',
- "subsetneq;": '\U0000228A',
- "subsetneqq;": '\U00002ACB',
- "subsim;": '\U00002AC7',
- "subsub;": '\U00002AD5',
- "subsup;": '\U00002AD3',
- "succ;": '\U0000227B',
- "succapprox;": '\U00002AB8',
- "succcurlyeq;": '\U0000227D',
- "succeq;": '\U00002AB0',
- "succnapprox;": '\U00002ABA',
- "succneqq;": '\U00002AB6',
- "succnsim;": '\U000022E9',
- "succsim;": '\U0000227F',
- "sum;": '\U00002211',
- "sung;": '\U0000266A',
- "sup;": '\U00002283',
- "sup1;": '\U000000B9',
- "sup2;": '\U000000B2',
- "sup3;": '\U000000B3',
- "supE;": '\U00002AC6',
- "supdot;": '\U00002ABE',
- "supdsub;": '\U00002AD8',
- "supe;": '\U00002287',
- "supedot;": '\U00002AC4',
- "suphsol;": '\U000027C9',
- "suphsub;": '\U00002AD7',
- "suplarr;": '\U0000297B',
- "supmult;": '\U00002AC2',
- "supnE;": '\U00002ACC',
- "supne;": '\U0000228B',
- "supplus;": '\U00002AC0',
- "supset;": '\U00002283',
- "supseteq;": '\U00002287',
- "supseteqq;": '\U00002AC6',
- "supsetneq;": '\U0000228B',
- "supsetneqq;": '\U00002ACC',
- "supsim;": '\U00002AC8',
- "supsub;": '\U00002AD4',
- "supsup;": '\U00002AD6',
- "swArr;": '\U000021D9',
- "swarhk;": '\U00002926',
- "swarr;": '\U00002199',
- "swarrow;": '\U00002199',
- "swnwar;": '\U0000292A',
- "szlig;": '\U000000DF',
- "target;": '\U00002316',
- "tau;": '\U000003C4',
- "tbrk;": '\U000023B4',
- "tcaron;": '\U00000165',
- "tcedil;": '\U00000163',
- "tcy;": '\U00000442',
- "tdot;": '\U000020DB',
- "telrec;": '\U00002315',
- "tfr;": '\U0001D531',
- "there4;": '\U00002234',
- "therefore;": '\U00002234',
- "theta;": '\U000003B8',
- "thetasym;": '\U000003D1',
- "thetav;": '\U000003D1',
- "thickapprox;": '\U00002248',
- "thicksim;": '\U0000223C',
- "thinsp;": '\U00002009',
- "thkap;": '\U00002248',
- "thksim;": '\U0000223C',
- "thorn;": '\U000000FE',
- "tilde;": '\U000002DC',
- "times;": '\U000000D7',
- "timesb;": '\U000022A0',
- "timesbar;": '\U00002A31',
- "timesd;": '\U00002A30',
- "tint;": '\U0000222D',
- "toea;": '\U00002928',
- "top;": '\U000022A4',
- "topbot;": '\U00002336',
- "topcir;": '\U00002AF1',
- "topf;": '\U0001D565',
- "topfork;": '\U00002ADA',
- "tosa;": '\U00002929',
- "tprime;": '\U00002034',
- "trade;": '\U00002122',
- "triangle;": '\U000025B5',
- "triangledown;": '\U000025BF',
- "triangleleft;": '\U000025C3',
- "trianglelefteq;": '\U000022B4',
- "triangleq;": '\U0000225C',
- "triangleright;": '\U000025B9',
- "trianglerighteq;": '\U000022B5',
- "tridot;": '\U000025EC',
- "trie;": '\U0000225C',
- "triminus;": '\U00002A3A',
- "triplus;": '\U00002A39',
- "trisb;": '\U000029CD',
- "tritime;": '\U00002A3B',
- "trpezium;": '\U000023E2',
- "tscr;": '\U0001D4C9',
- "tscy;": '\U00000446',
- "tshcy;": '\U0000045B',
- "tstrok;": '\U00000167',
- "twixt;": '\U0000226C',
- "twoheadleftarrow;": '\U0000219E',
- "twoheadrightarrow;": '\U000021A0',
- "uArr;": '\U000021D1',
- "uHar;": '\U00002963',
- "uacute;": '\U000000FA',
- "uarr;": '\U00002191',
- "ubrcy;": '\U0000045E',
- "ubreve;": '\U0000016D',
- "ucirc;": '\U000000FB',
- "ucy;": '\U00000443',
- "udarr;": '\U000021C5',
- "udblac;": '\U00000171',
- "udhar;": '\U0000296E',
- "ufisht;": '\U0000297E',
- "ufr;": '\U0001D532',
- "ugrave;": '\U000000F9',
- "uharl;": '\U000021BF',
- "uharr;": '\U000021BE',
- "uhblk;": '\U00002580',
- "ulcorn;": '\U0000231C',
- "ulcorner;": '\U0000231C',
- "ulcrop;": '\U0000230F',
- "ultri;": '\U000025F8',
- "umacr;": '\U0000016B',
- "uml;": '\U000000A8',
- "uogon;": '\U00000173',
- "uopf;": '\U0001D566',
- "uparrow;": '\U00002191',
- "updownarrow;": '\U00002195',
- "upharpoonleft;": '\U000021BF',
- "upharpoonright;": '\U000021BE',
- "uplus;": '\U0000228E',
- "upsi;": '\U000003C5',
- "upsih;": '\U000003D2',
- "upsilon;": '\U000003C5',
- "upuparrows;": '\U000021C8',
- "urcorn;": '\U0000231D',
- "urcorner;": '\U0000231D',
- "urcrop;": '\U0000230E',
- "uring;": '\U0000016F',
- "urtri;": '\U000025F9',
- "uscr;": '\U0001D4CA',
- "utdot;": '\U000022F0',
- "utilde;": '\U00000169',
- "utri;": '\U000025B5',
- "utrif;": '\U000025B4',
- "uuarr;": '\U000021C8',
- "uuml;": '\U000000FC',
- "uwangle;": '\U000029A7',
- "vArr;": '\U000021D5',
- "vBar;": '\U00002AE8',
- "vBarv;": '\U00002AE9',
- "vDash;": '\U000022A8',
- "vangrt;": '\U0000299C',
- "varepsilon;": '\U000003F5',
- "varkappa;": '\U000003F0',
- "varnothing;": '\U00002205',
- "varphi;": '\U000003D5',
- "varpi;": '\U000003D6',
- "varpropto;": '\U0000221D',
- "varr;": '\U00002195',
- "varrho;": '\U000003F1',
- "varsigma;": '\U000003C2',
- "vartheta;": '\U000003D1',
- "vartriangleleft;": '\U000022B2',
- "vartriangleright;": '\U000022B3',
- "vcy;": '\U00000432',
- "vdash;": '\U000022A2',
- "vee;": '\U00002228',
- "veebar;": '\U000022BB',
- "veeeq;": '\U0000225A',
- "vellip;": '\U000022EE',
- "verbar;": '\U0000007C',
- "vert;": '\U0000007C',
- "vfr;": '\U0001D533',
- "vltri;": '\U000022B2',
- "vopf;": '\U0001D567',
- "vprop;": '\U0000221D',
- "vrtri;": '\U000022B3',
- "vscr;": '\U0001D4CB',
- "vzigzag;": '\U0000299A',
- "wcirc;": '\U00000175',
- "wedbar;": '\U00002A5F',
- "wedge;": '\U00002227',
- "wedgeq;": '\U00002259',
- "weierp;": '\U00002118',
- "wfr;": '\U0001D534',
- "wopf;": '\U0001D568',
- "wp;": '\U00002118',
- "wr;": '\U00002240',
- "wreath;": '\U00002240',
- "wscr;": '\U0001D4CC',
- "xcap;": '\U000022C2',
- "xcirc;": '\U000025EF',
- "xcup;": '\U000022C3',
- "xdtri;": '\U000025BD',
- "xfr;": '\U0001D535',
- "xhArr;": '\U000027FA',
- "xharr;": '\U000027F7',
- "xi;": '\U000003BE',
- "xlArr;": '\U000027F8',
- "xlarr;": '\U000027F5',
- "xmap;": '\U000027FC',
- "xnis;": '\U000022FB',
- "xodot;": '\U00002A00',
- "xopf;": '\U0001D569',
- "xoplus;": '\U00002A01',
- "xotime;": '\U00002A02',
- "xrArr;": '\U000027F9',
- "xrarr;": '\U000027F6',
- "xscr;": '\U0001D4CD',
- "xsqcup;": '\U00002A06',
- "xuplus;": '\U00002A04',
- "xutri;": '\U000025B3',
- "xvee;": '\U000022C1',
- "xwedge;": '\U000022C0',
- "yacute;": '\U000000FD',
- "yacy;": '\U0000044F',
- "ycirc;": '\U00000177',
- "ycy;": '\U0000044B',
- "yen;": '\U000000A5',
- "yfr;": '\U0001D536',
- "yicy;": '\U00000457',
- "yopf;": '\U0001D56A',
- "yscr;": '\U0001D4CE',
- "yucy;": '\U0000044E',
- "yuml;": '\U000000FF',
- "zacute;": '\U0000017A',
- "zcaron;": '\U0000017E',
- "zcy;": '\U00000437',
- "zdot;": '\U0000017C',
- "zeetrf;": '\U00002128',
- "zeta;": '\U000003B6',
- "zfr;": '\U0001D537',
- "zhcy;": '\U00000436',
- "zigrarr;": '\U000021DD',
- "zopf;": '\U0001D56B',
- "zscr;": '\U0001D4CF',
- "zwj;": '\U0000200D',
- "zwnj;": '\U0000200C',
- "AElig": '\U000000C6',
- "AMP": '\U00000026',
- "Aacute": '\U000000C1',
- "Acirc": '\U000000C2',
- "Agrave": '\U000000C0',
- "Aring": '\U000000C5',
- "Atilde": '\U000000C3',
- "Auml": '\U000000C4',
- "COPY": '\U000000A9',
- "Ccedil": '\U000000C7',
- "ETH": '\U000000D0',
- "Eacute": '\U000000C9',
- "Ecirc": '\U000000CA',
- "Egrave": '\U000000C8',
- "Euml": '\U000000CB',
- "GT": '\U0000003E',
- "Iacute": '\U000000CD',
- "Icirc": '\U000000CE',
- "Igrave": '\U000000CC',
- "Iuml": '\U000000CF',
- "LT": '\U0000003C',
- "Ntilde": '\U000000D1',
- "Oacute": '\U000000D3',
- "Ocirc": '\U000000D4',
- "Ograve": '\U000000D2',
- "Oslash": '\U000000D8',
- "Otilde": '\U000000D5',
- "Ouml": '\U000000D6',
- "QUOT": '\U00000022',
- "REG": '\U000000AE',
- "THORN": '\U000000DE',
- "Uacute": '\U000000DA',
- "Ucirc": '\U000000DB',
- "Ugrave": '\U000000D9',
- "Uuml": '\U000000DC',
- "Yacute": '\U000000DD',
- "aacute": '\U000000E1',
- "acirc": '\U000000E2',
- "acute": '\U000000B4',
- "aelig": '\U000000E6',
- "agrave": '\U000000E0',
- "amp": '\U00000026',
- "aring": '\U000000E5',
- "atilde": '\U000000E3',
- "auml": '\U000000E4',
- "brvbar": '\U000000A6',
- "ccedil": '\U000000E7',
- "cedil": '\U000000B8',
- "cent": '\U000000A2',
- "copy": '\U000000A9',
- "curren": '\U000000A4',
- "deg": '\U000000B0',
- "divide": '\U000000F7',
- "eacute": '\U000000E9',
- "ecirc": '\U000000EA',
- "egrave": '\U000000E8',
- "eth": '\U000000F0',
- "euml": '\U000000EB',
- "frac12": '\U000000BD',
- "frac14": '\U000000BC',
- "frac34": '\U000000BE',
- "gt": '\U0000003E',
- "iacute": '\U000000ED',
- "icirc": '\U000000EE',
- "iexcl": '\U000000A1',
- "igrave": '\U000000EC',
- "iquest": '\U000000BF',
- "iuml": '\U000000EF',
- "laquo": '\U000000AB',
- "lt": '\U0000003C',
- "macr": '\U000000AF',
- "micro": '\U000000B5',
- "middot": '\U000000B7',
- "nbsp": '\U000000A0',
- "not": '\U000000AC',
- "ntilde": '\U000000F1',
- "oacute": '\U000000F3',
- "ocirc": '\U000000F4',
- "ograve": '\U000000F2',
- "ordf": '\U000000AA',
- "ordm": '\U000000BA',
- "oslash": '\U000000F8',
- "otilde": '\U000000F5',
- "ouml": '\U000000F6',
- "para": '\U000000B6',
- "plusmn": '\U000000B1',
- "pound": '\U000000A3',
- "quot": '\U00000022',
- "raquo": '\U000000BB',
- "reg": '\U000000AE',
- "sect": '\U000000A7',
- "shy": '\U000000AD',
- "sup1": '\U000000B9',
- "sup2": '\U000000B2',
- "sup3": '\U000000B3',
- "szlig": '\U000000DF',
- "thorn": '\U000000FE',
- "times": '\U000000D7',
- "uacute": '\U000000FA',
- "ucirc": '\U000000FB',
- "ugrave": '\U000000F9',
- "uml": '\U000000A8',
- "uuml": '\U000000FC',
- "yacute": '\U000000FD',
- "yen": '\U000000A5',
- "yuml": '\U000000FF',
-}
-
-// HTML entities that are two unicode codepoints.
-var entity2 = map[string][2]rune{
- // TODO(nigeltao): Handle replacements that are wider than their names.
- // "nLt;": {'\u226A', '\u20D2'},
- // "nGt;": {'\u226B', '\u20D2'},
- "NotEqualTilde;": {'\u2242', '\u0338'},
- "NotGreaterFullEqual;": {'\u2267', '\u0338'},
- "NotGreaterGreater;": {'\u226B', '\u0338'},
- "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'},
- "NotHumpDownHump;": {'\u224E', '\u0338'},
- "NotHumpEqual;": {'\u224F', '\u0338'},
- "NotLeftTriangleBar;": {'\u29CF', '\u0338'},
- "NotLessLess;": {'\u226A', '\u0338'},
- "NotLessSlantEqual;": {'\u2A7D', '\u0338'},
- "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'},
- "NotNestedLessLess;": {'\u2AA1', '\u0338'},
- "NotPrecedesEqual;": {'\u2AAF', '\u0338'},
- "NotRightTriangleBar;": {'\u29D0', '\u0338'},
- "NotSquareSubset;": {'\u228F', '\u0338'},
- "NotSquareSuperset;": {'\u2290', '\u0338'},
- "NotSubset;": {'\u2282', '\u20D2'},
- "NotSucceedsEqual;": {'\u2AB0', '\u0338'},
- "NotSucceedsTilde;": {'\u227F', '\u0338'},
- "NotSuperset;": {'\u2283', '\u20D2'},
- "ThickSpace;": {'\u205F', '\u200A'},
- "acE;": {'\u223E', '\u0333'},
- "bne;": {'\u003D', '\u20E5'},
- "bnequiv;": {'\u2261', '\u20E5'},
- "caps;": {'\u2229', '\uFE00'},
- "cups;": {'\u222A', '\uFE00'},
- "fjlig;": {'\u0066', '\u006A'},
- "gesl;": {'\u22DB', '\uFE00'},
- "gvertneqq;": {'\u2269', '\uFE00'},
- "gvnE;": {'\u2269', '\uFE00'},
- "lates;": {'\u2AAD', '\uFE00'},
- "lesg;": {'\u22DA', '\uFE00'},
- "lvertneqq;": {'\u2268', '\uFE00'},
- "lvnE;": {'\u2268', '\uFE00'},
- "nGg;": {'\u22D9', '\u0338'},
- "nGtv;": {'\u226B', '\u0338'},
- "nLl;": {'\u22D8', '\u0338'},
- "nLtv;": {'\u226A', '\u0338'},
- "nang;": {'\u2220', '\u20D2'},
- "napE;": {'\u2A70', '\u0338'},
- "napid;": {'\u224B', '\u0338'},
- "nbump;": {'\u224E', '\u0338'},
- "nbumpe;": {'\u224F', '\u0338'},
- "ncongdot;": {'\u2A6D', '\u0338'},
- "nedot;": {'\u2250', '\u0338'},
- "nesim;": {'\u2242', '\u0338'},
- "ngE;": {'\u2267', '\u0338'},
- "ngeqq;": {'\u2267', '\u0338'},
- "ngeqslant;": {'\u2A7E', '\u0338'},
- "nges;": {'\u2A7E', '\u0338'},
- "nlE;": {'\u2266', '\u0338'},
- "nleqq;": {'\u2266', '\u0338'},
- "nleqslant;": {'\u2A7D', '\u0338'},
- "nles;": {'\u2A7D', '\u0338'},
- "notinE;": {'\u22F9', '\u0338'},
- "notindot;": {'\u22F5', '\u0338'},
- "nparsl;": {'\u2AFD', '\u20E5'},
- "npart;": {'\u2202', '\u0338'},
- "npre;": {'\u2AAF', '\u0338'},
- "npreceq;": {'\u2AAF', '\u0338'},
- "nrarrc;": {'\u2933', '\u0338'},
- "nrarrw;": {'\u219D', '\u0338'},
- "nsce;": {'\u2AB0', '\u0338'},
- "nsubE;": {'\u2AC5', '\u0338'},
- "nsubset;": {'\u2282', '\u20D2'},
- "nsubseteqq;": {'\u2AC5', '\u0338'},
- "nsucceq;": {'\u2AB0', '\u0338'},
- "nsupE;": {'\u2AC6', '\u0338'},
- "nsupset;": {'\u2283', '\u20D2'},
- "nsupseteqq;": {'\u2AC6', '\u0338'},
- "nvap;": {'\u224D', '\u20D2'},
- "nvge;": {'\u2265', '\u20D2'},
- "nvgt;": {'\u003E', '\u20D2'},
- "nvle;": {'\u2264', '\u20D2'},
- "nvlt;": {'\u003C', '\u20D2'},
- "nvltrie;": {'\u22B4', '\u20D2'},
- "nvrtrie;": {'\u22B5', '\u20D2'},
- "nvsim;": {'\u223C', '\u20D2'},
- "race;": {'\u223D', '\u0331'},
- "smtes;": {'\u2AAC', '\uFE00'},
- "sqcaps;": {'\u2293', '\uFE00'},
- "sqcups;": {'\u2294', '\uFE00'},
- "varsubsetneq;": {'\u228A', '\uFE00'},
- "varsubsetneqq;": {'\u2ACB', '\uFE00'},
- "varsupsetneq;": {'\u228B', '\uFE00'},
- "varsupsetneqq;": {'\u2ACC', '\uFE00'},
- "vnsub;": {'\u2282', '\u20D2'},
- "vnsup;": {'\u2283', '\u20D2'},
- "vsubnE;": {'\u2ACB', '\uFE00'},
- "vsubne;": {'\u228A', '\uFE00'},
- "vsupnE;": {'\u2ACC', '\uFE00'},
- "vsupne;": {'\u228B', '\uFE00'},
-}
diff --git a/src/pkg/html/entity_test.go b/src/pkg/html/entity_test.go
deleted file mode 100644
index b53f866fa..000000000
--- a/src/pkg/html/entity_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package html
-
-import (
- "testing"
- "unicode/utf8"
-)
-
-func TestEntityLength(t *testing.T) {
- // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key).
- // The +1 comes from the leading "&". This property implies that the length of
- // unescaped text is <= the length of escaped text.
- for k, v := range entity {
- if 1+len(k) < utf8.RuneLen(v) {
- t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v))
- }
- if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' {
- t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon)
- }
- }
- for k, v := range entity2 {
- if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) {
- t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1]))
- }
- }
-}
diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go
deleted file mode 100644
index dd5dfa7cd..000000000
--- a/src/pkg/html/escape.go
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package html provides functions for escaping and unescaping HTML text.
-package html
-
-import (
- "bytes"
- "strings"
- "unicode/utf8"
-)
-
-type writer interface {
- WriteString(string) (int, error)
-}
-
-// These replacements permit compatibility with old numeric entities that
-// assumed Windows-1252 encoding.
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
-var replacementTable = [...]rune{
- '\u20AC', // First entry is what 0x80 should be replaced with.
- '\u0081',
- '\u201A',
- '\u0192',
- '\u201E',
- '\u2026',
- '\u2020',
- '\u2021',
- '\u02C6',
- '\u2030',
- '\u0160',
- '\u2039',
- '\u0152',
- '\u008D',
- '\u017D',
- '\u008F',
- '\u0090',
- '\u2018',
- '\u2019',
- '\u201C',
- '\u201D',
- '\u2022',
- '\u2013',
- '\u2014',
- '\u02DC',
- '\u2122',
- '\u0161',
- '\u203A',
- '\u0153',
- '\u009D',
- '\u017E',
- '\u0178', // Last entry is 0x9F.
- // 0x00->'\uFFFD' is handled programmatically.
- // 0x0D->'\u000D' is a no-op.
-}
-
-// unescapeEntity reads an entity like "&lt;" from b[src:] and writes the
-// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
-// Precondition: b[src] == '&' && dst <= src.
-// attribute should be true if parsing an attribute value.
-func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
-
- // i starts at 1 because we already know that s[0] == '&'.
- i, s := 1, b[src:]
-
- if len(s) <= 1 {
- b[dst] = b[src]
- return dst + 1, src + 1
- }
-
- if s[i] == '#' {
- if len(s) <= 3 { // We need to have at least "&#.".
- b[dst] = b[src]
- return dst + 1, src + 1
- }
- i++
- c := s[i]
- hex := false
- if c == 'x' || c == 'X' {
- hex = true
- i++
- }
-
- x := '\x00'
- for i < len(s) {
- c = s[i]
- i++
- if hex {
- if '0' <= c && c <= '9' {
- x = 16*x + rune(c) - '0'
- continue
- } else if 'a' <= c && c <= 'f' {
- x = 16*x + rune(c) - 'a' + 10
- continue
- } else if 'A' <= c && c <= 'F' {
- x = 16*x + rune(c) - 'A' + 10
- continue
- }
- } else if '0' <= c && c <= '9' {
- x = 10*x + rune(c) - '0'
- continue
- }
- if c != ';' {
- i--
- }
- break
- }
-
- if i <= 3 { // No characters matched.
- b[dst] = b[src]
- return dst + 1, src + 1
- }
-
- if 0x80 <= x && x <= 0x9F {
- // Replace characters from Windows-1252 with UTF-8 equivalents.
- x = replacementTable[x-0x80]
- } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
- // Replace invalid characters with the replacement character.
- x = '\uFFFD'
- }
-
- return dst + utf8.EncodeRune(b[dst:], x), src + i
- }
-
- // Consume the maximum number of characters possible, with the
- // consumed characters matching one of the named references.
-
- for i < len(s) {
- c := s[i]
- i++
- // Lower-cased characters are more common in entities, so we check for them first.
- if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
- continue
- }
- if c != ';' {
- i--
- }
- break
- }
-
- entityName := string(s[1:i])
- if entityName == "" {
- // No-op.
- } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
- // No-op.
- } else if x := entity[entityName]; x != 0 {
- return dst + utf8.EncodeRune(b[dst:], x), src + i
- } else if x := entity2[entityName]; x[0] != 0 {
- dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
- return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
- } else if !attribute {
- maxLen := len(entityName) - 1
- if maxLen > longestEntityWithoutSemicolon {
- maxLen = longestEntityWithoutSemicolon
- }
- for j := maxLen; j > 1; j-- {
- if x := entity[entityName[:j]]; x != 0 {
- return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
- }
- }
- }
-
- dst1, src1 = dst+i, src+i
- copy(b[dst:dst1], b[src:src1])
- return dst1, src1
-}
-
-// unescape unescapes b's entities in-place, so that "a&lt;b" becomes "a<b".
-func unescape(b []byte) []byte {
- for i, c := range b {
- if c == '&' {
- dst, src := unescapeEntity(b, i, i, false)
- for src < len(b) {
- c := b[src]
- if c == '&' {
- dst, src = unescapeEntity(b, dst, src, false)
- } else {
- b[dst] = c
- dst, src = dst+1, src+1
- }
- }
- return b[0:dst]
- }
- }
- return b
-}
-
-const escapedChars = `&'<>"`
-
-func escape(w writer, s string) error {
- i := strings.IndexAny(s, escapedChars)
- for i != -1 {
- if _, err := w.WriteString(s[:i]); err != nil {
- return err
- }
- var esc string
- switch s[i] {
- case '&':
- esc = "&amp;"
- case '\'':
- // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
- esc = "&#39;"
- case '<':
- esc = "&lt;"
- case '>':
- esc = "&gt;"
- case '"':
- // "&#34;" is shorter than "&quot;".
- esc = "&#34;"
- default:
- panic("unrecognized escape character")
- }
- s = s[i+1:]
- if _, err := w.WriteString(esc); err != nil {
- return err
- }
- i = strings.IndexAny(s, escapedChars)
- }
- _, err := w.WriteString(s)
- return err
-}
-
-// EscapeString escapes special characters like "<" to become "&lt;". It
-// escapes only five such characters: <, >, &, ' and ".
-// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
-// always true.
-func EscapeString(s string) string {
- if strings.IndexAny(s, escapedChars) == -1 {
- return s
- }
- var buf bytes.Buffer
- escape(&buf, s)
- return buf.String()
-}
-
-// UnescapeString unescapes entities like "&lt;" to become "<". It unescapes a
-// larger range of entities than EscapeString escapes. For example, "&aacute;"
-// unescapes to "á", as does "&#225;" and "&xE1;".
-// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
-// always true.
-func UnescapeString(s string) string {
- for _, c := range s {
- if c == '&' {
- return string(unescape([]byte(s)))
- }
- }
- return s
-}
diff --git a/src/pkg/html/escape_test.go b/src/pkg/html/escape_test.go
deleted file mode 100644
index 2d7ad8ac2..000000000
--- a/src/pkg/html/escape_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package html
-
-import "testing"
-
-type unescapeTest struct {
- // A short description of the test case.
- desc string
- // The HTML text.
- html string
- // The unescaped text.
- unescaped string
-}
-
-var unescapeTests = []unescapeTest{
- // Handle no entities.
- {
- "copy",
- "A\ttext\nstring",
- "A\ttext\nstring",
- },
- // Handle simple named entities.
- {
- "simple",
- "&amp; &gt; &lt;",
- "& > <",
- },
- // Handle hitting the end of the string.
- {
- "stringEnd",
- "&amp &amp",
- "& &",
- },
- // Handle entities with two codepoints.
- {
- "multiCodepoint",
- "text &gesl; blah",
- "text \u22db\ufe00 blah",
- },
- // Handle decimal numeric entities.
- {
- "decimalEntity",
- "Delta = &#916; ",
- "Delta = Δ ",
- },
- // Handle hexadecimal numeric entities.
- {
- "hexadecimalEntity",
- "Lambda = &#x3bb; = &#X3Bb ",
- "Lambda = λ = λ ",
- },
- // Handle numeric early termination.
- {
- "numericEnds",
- "&# &#x &#128;43 &copy = &#169f = &#xa9",
- "&# &#x €43 © = ©f = ©",
- },
- // Handle numeric ISO-8859-1 entity replacements.
- {
- "numericReplacements",
- "Footnote&#x87;",
- "Footnote‡",
- },
- // Handle single ampersand.
- {
- "copySingleAmpersand",
- "&",
- "&",
- },
- // Handle ampersand followed by non-entity.
- {
- "copyAmpersandNonEntity",
- "text &test",
- "text &test",
- },
- // Handle "&#".
- {
- "copyAmpersandHash",
- "text &#",
- "text &#",
- },
-}
-
-func TestUnescape(t *testing.T) {
- for _, tt := range unescapeTests {
- unescaped := UnescapeString(tt.html)
- if unescaped != tt.unescaped {
- t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
- }
- }
-}
-
-func TestUnescapeEscape(t *testing.T) {
- ss := []string{
- ``,
- `abc def`,
- `a & b`,
- `a&amp;b`,
- `a &amp b`,
- `&quot;`,
- `"`,
- `"<&>"`,
- `&quot;&lt;&amp;&gt;&quot;`,
- `3&5==1 && 0<1, "0&lt;1", a+acute=&aacute;`,
- `The special characters are: <, >, &, ' and "`,
- }
- for _, s := range ss {
- if got := UnescapeString(EscapeString(s)); got != s {
- t.Errorf("got %q want %q", got, s)
- }
- }
-}
diff --git a/src/pkg/html/template/attr.go b/src/pkg/html/template/attr.go
deleted file mode 100644
index d65d34007..000000000
--- a/src/pkg/html/template/attr.go
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "strings"
-)
-
-// attrTypeMap[n] describes the value of the given attribute.
-// If an attribute affects (or can mask) the encoding or interpretation of
-// other content, or affects the contents, idempotency, or credentials of a
-// network message, then the value in this map is contentTypeUnsafe.
-// This map is derived from HTML5, specifically
-// http://www.w3.org/TR/html5/Overview.html#attributes-1
-// as well as "%URI"-typed attributes from
-// http://www.w3.org/TR/html4/index/attributes.html
-var attrTypeMap = map[string]contentType{
- "accept": contentTypePlain,
- "accept-charset": contentTypeUnsafe,
- "action": contentTypeURL,
- "alt": contentTypePlain,
- "archive": contentTypeURL,
- "async": contentTypeUnsafe,
- "autocomplete": contentTypePlain,
- "autofocus": contentTypePlain,
- "autoplay": contentTypePlain,
- "background": contentTypeURL,
- "border": contentTypePlain,
- "checked": contentTypePlain,
- "cite": contentTypeURL,
- "challenge": contentTypeUnsafe,
- "charset": contentTypeUnsafe,
- "class": contentTypePlain,
- "classid": contentTypeURL,
- "codebase": contentTypeURL,
- "cols": contentTypePlain,
- "colspan": contentTypePlain,
- "content": contentTypeUnsafe,
- "contenteditable": contentTypePlain,
- "contextmenu": contentTypePlain,
- "controls": contentTypePlain,
- "coords": contentTypePlain,
- "crossorigin": contentTypeUnsafe,
- "data": contentTypeURL,
- "datetime": contentTypePlain,
- "default": contentTypePlain,
- "defer": contentTypeUnsafe,
- "dir": contentTypePlain,
- "dirname": contentTypePlain,
- "disabled": contentTypePlain,
- "draggable": contentTypePlain,
- "dropzone": contentTypePlain,
- "enctype": contentTypeUnsafe,
- "for": contentTypePlain,
- "form": contentTypeUnsafe,
- "formaction": contentTypeURL,
- "formenctype": contentTypeUnsafe,
- "formmethod": contentTypeUnsafe,
- "formnovalidate": contentTypeUnsafe,
- "formtarget": contentTypePlain,
- "headers": contentTypePlain,
- "height": contentTypePlain,
- "hidden": contentTypePlain,
- "high": contentTypePlain,
- "href": contentTypeURL,
- "hreflang": contentTypePlain,
- "http-equiv": contentTypeUnsafe,
- "icon": contentTypeURL,
- "id": contentTypePlain,
- "ismap": contentTypePlain,
- "keytype": contentTypeUnsafe,
- "kind": contentTypePlain,
- "label": contentTypePlain,
- "lang": contentTypePlain,
- "language": contentTypeUnsafe,
- "list": contentTypePlain,
- "longdesc": contentTypeURL,
- "loop": contentTypePlain,
- "low": contentTypePlain,
- "manifest": contentTypeURL,
- "max": contentTypePlain,
- "maxlength": contentTypePlain,
- "media": contentTypePlain,
- "mediagroup": contentTypePlain,
- "method": contentTypeUnsafe,
- "min": contentTypePlain,
- "multiple": contentTypePlain,
- "name": contentTypePlain,
- "novalidate": contentTypeUnsafe,
- // Skip handler names from
- // http://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
- // since we have special handling in attrType.
- "open": contentTypePlain,
- "optimum": contentTypePlain,
- "pattern": contentTypeUnsafe,
- "placeholder": contentTypePlain,
- "poster": contentTypeURL,
- "profile": contentTypeURL,
- "preload": contentTypePlain,
- "pubdate": contentTypePlain,
- "radiogroup": contentTypePlain,
- "readonly": contentTypePlain,
- "rel": contentTypeUnsafe,
- "required": contentTypePlain,
- "reversed": contentTypePlain,
- "rows": contentTypePlain,
- "rowspan": contentTypePlain,
- "sandbox": contentTypeUnsafe,
- "spellcheck": contentTypePlain,
- "scope": contentTypePlain,
- "scoped": contentTypePlain,
- "seamless": contentTypePlain,
- "selected": contentTypePlain,
- "shape": contentTypePlain,
- "size": contentTypePlain,
- "sizes": contentTypePlain,
- "span": contentTypePlain,
- "src": contentTypeURL,
- "srcdoc": contentTypeHTML,
- "srclang": contentTypePlain,
- "start": contentTypePlain,
- "step": contentTypePlain,
- "style": contentTypeCSS,
- "tabindex": contentTypePlain,
- "target": contentTypePlain,
- "title": contentTypePlain,
- "type": contentTypeUnsafe,
- "usemap": contentTypeURL,
- "value": contentTypeUnsafe,
- "width": contentTypePlain,
- "wrap": contentTypePlain,
- "xmlns": contentTypeURL,
-}
-
-// attrType returns a conservative (upper-bound on authority) guess at the
-// type of the named attribute.
-func attrType(name string) contentType {
- name = strings.ToLower(name)
- if strings.HasPrefix(name, "data-") {
- // Strip data- so that custom attribute heuristics below are
- // widely applied.
- // Treat data-action as URL below.
- name = name[5:]
- } else if colon := strings.IndexRune(name, ':'); colon != -1 {
- if name[:colon] == "xmlns" {
- return contentTypeURL
- }
- // Treat svg:href and xlink:href as href below.
- name = name[colon+1:]
- }
- if t, ok := attrTypeMap[name]; ok {
- return t
- }
- // Treat partial event handler names as script.
- if strings.HasPrefix(name, "on") {
- return contentTypeJS
- }
-
- // Heuristics to prevent "javascript:..." injection in custom
- // data attributes and custom attributes like g:tweetUrl.
- // http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
- // "Custom data attributes are intended to store custom data
- // private to the page or application, for which there are no
- // more appropriate attributes or elements."
- // Developers seem to store URL content in data URLs that start
- // or end with "URI" or "URL".
- if strings.Contains(name, "src") ||
- strings.Contains(name, "uri") ||
- strings.Contains(name, "url") {
- return contentTypeURL
- }
- return contentTypePlain
-}
diff --git a/src/pkg/html/template/clone_test.go b/src/pkg/html/template/clone_test.go
deleted file mode 100644
index e11bff2c5..000000000
--- a/src/pkg/html/template/clone_test.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "errors"
- "io/ioutil"
- "testing"
- "text/template/parse"
-)
-
-func TestAddParseTree(t *testing.T) {
- root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
- tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
- if err != nil {
- t.Fatal(err)
- }
- added := Must(root.AddParseTree("b", tree["b"]))
- b := new(bytes.Buffer)
- err = added.ExecuteTemplate(b, "a", "1>0")
- if err != nil {
- t.Fatal(err)
- }
- if got, want := b.String(), ` 1&gt;0 <a href=" 1%3e0 "></a>`; got != want {
- t.Errorf("got %q want %q", got, want)
- }
-}
-
-func TestClone(t *testing.T) {
- // The {{.}} will be executed with data "<i>*/" in different contexts.
- // In the t0 template, it will be in a text context.
- // In the t1 template, it will be in a URL context.
- // In the t2 template, it will be in a JavaScript context.
- // In the t3 template, it will be in a CSS context.
- const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
- b := new(bytes.Buffer)
-
- // Create an incomplete template t0.
- t0 := Must(New("t0").Parse(tmpl))
-
- // Clone t0 as t1.
- t1 := Must(t0.Clone())
- Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`))
- Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`))
-
- // Execute t1.
- b.Reset()
- if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
- t.Fatal(err)
- }
- if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want {
- t.Errorf("t1: got %q want %q", got, want)
- }
-
- // Clone t0 as t2.
- t2 := Must(t0.Clone())
- Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`))
- Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`))
-
- // Execute t2.
- b.Reset()
- if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
- t.Fatal(err)
- }
- if got, want := b.String(), ` <p onclick="javascript: &#34;\u003ci\u003e*/&#34; "></p> `; got != want {
- t.Errorf("t2: got %q want %q", got, want)
- }
-
- // Clone t0 as t3, but do not execute t3 yet.
- t3 := Must(t0.Clone())
- Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`))
- Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`))
-
- // Complete t0.
- Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
- Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
-
- // Clone t0 as t4. Redefining the "lhs" template should fail.
- t4 := Must(t0.Clone())
- if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
- t.Error(`redefine "lhs": got nil err want non-nil`)
- }
-
- // Execute t0.
- b.Reset()
- if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
- t.Fatal(err)
- }
- if got, want := b.String(), ` ( &lt;i&gt;*/ ) `; got != want {
- t.Errorf("t0: got %q want %q", got, want)
- }
-
- // Clone t0. This should fail, as t0 has already executed.
- if _, err := t0.Clone(); err == nil {
- t.Error(`t0.Clone(): got nil err want non-nil`)
- }
-
- // Similarly, cloning sub-templates should fail.
- if _, err := t0.Lookup("a").Clone(); err == nil {
- t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
- }
- if _, err := t0.Lookup("lhs").Clone(); err == nil {
- t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
- }
-
- // Execute t3.
- b.Reset()
- if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
- t.Fatal(err)
- }
- if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want {
- t.Errorf("t3: got %q want %q", got, want)
- }
-}
-
-func TestTemplates(t *testing.T) {
- names := []string{"t0", "a", "lhs", "rhs"}
- // Some template definitions borrowed from TestClone.
- const tmpl = `
- {{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}
- {{define "lhs"}} <a href=" {{end}}
- {{define "rhs"}} "></a> {{end}}`
- t0 := Must(New("t0").Parse(tmpl))
- templates := t0.Templates()
- if len(templates) != len(names) {
- t.Errorf("expected %d templates; got %d", len(names), len(templates))
- }
- for _, name := range names {
- found := false
- for _, tmpl := range templates {
- if name == tmpl.text.Name() {
- found = true
- break
- }
- }
- if !found {
- t.Error("could not find template", name)
- }
- }
-}
-
-// This used to crash; http://golang.org/issue/3281
-func TestCloneCrash(t *testing.T) {
- t1 := New("all")
- Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`))
- t1.Clone()
-}
-
-// Ensure that this guarantee from the docs is upheld:
-// "Further calls to Parse in the copy will add templates
-// to the copy but not to the original."
-func TestCloneThenParse(t *testing.T) {
- t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`))
- t1 := Must(t0.Clone())
- Must(t1.Parse(`{{define "embedded"}}t1{{end}}`))
- if len(t0.Templates())+1 != len(t1.Templates()) {
- t.Error("adding a template to a clone added it to the original")
- }
- // double check that the embedded template isn't available in the original
- err := t0.ExecuteTemplate(ioutil.Discard, "a", nil)
- if err == nil {
- t.Error("expected 'no such template' error")
- }
-}
-
-// https://code.google.com/p/go/issues/detail?id=5980
-func TestFuncMapWorksAfterClone(t *testing.T) {
- funcs := FuncMap{"customFunc": func() (string, error) {
- return "", errors.New("issue5980")
- }}
-
- // get the expected error output (no clone)
- uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
- wantErr := uncloned.Execute(ioutil.Discard, nil)
-
- // toClone must be the same as uncloned. It has to be recreated from scratch,
- // since cloning cannot occur after execution.
- toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
- cloned := Must(toClone.Clone())
- gotErr := cloned.Execute(ioutil.Discard, nil)
-
- if wantErr.Error() != gotErr.Error() {
- t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
- }
-}
diff --git a/src/pkg/html/template/content.go b/src/pkg/html/template/content.go
deleted file mode 100644
index 3715ed5c9..000000000
--- a/src/pkg/html/template/content.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "reflect"
-)
-
-// Strings of content from a trusted source.
-type (
- // CSS encapsulates known safe content that matches any of:
- // 1. The CSS3 stylesheet production, such as `p { color: purple }`.
- // 2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
- // 3. CSS3 declaration productions, such as `color: red; margin: 2px`.
- // 4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
- // See http://www.w3.org/TR/css3-syntax/#parsing and
- // https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
- CSS string
-
- // HTML encapsulates a known safe HTML document fragment.
- // It should not be used for HTML from a third-party, or HTML with
- // unclosed tags or comments. The outputs of a sound HTML sanitizer
- // and a template escaped by this package are fine for use with HTML.
- HTML string
-
- // HTMLAttr encapsulates an HTML attribute from a trusted source,
- // for example, ` dir="ltr"`.
- HTMLAttr string
-
- // JS encapsulates a known safe EcmaScript5 Expression, for example,
- // `(x + y * z())`.
- // Template authors are responsible for ensuring that typed expressions
- // do not break the intended precedence and that there is no
- // statement/expression ambiguity as when passing an expression like
- // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
- // valid Program with a very different meaning.
- JS string
-
- // JSStr encapsulates a sequence of characters meant to be embedded
- // between quotes in a JavaScript expression.
- // The string must match a series of StringCharacters:
- // StringCharacter :: SourceCharacter but not `\` or LineTerminator
- // | EscapeSequence
- // Note that LineContinuations are not allowed.
- // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
- JSStr string
-
- // URL encapsulates a known safe URL or URL substring (see RFC 3986).
- // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
- // from a trusted source should go in the page, but by default dynamic
- // `javascript:` URLs are filtered out since they are a frequently
- // exploited injection vector.
- URL string
-)
-
-type contentType uint8
-
-const (
- contentTypePlain contentType = iota
- contentTypeCSS
- contentTypeHTML
- contentTypeHTMLAttr
- contentTypeJS
- contentTypeJSStr
- contentTypeURL
- // contentTypeUnsafe is used in attr.go for values that affect how
- // embedded content and network messages are formed, vetted,
- // or interpreted; or which credentials network messages carry.
- contentTypeUnsafe
-)
-
-// indirect returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil).
-func indirect(a interface{}) interface{} {
- if a == nil {
- return nil
- }
- if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
- // Avoid creating a reflect.Value if it's not a pointer.
- return a
- }
- v := reflect.ValueOf(a)
- for v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-var (
- errorType = reflect.TypeOf((*error)(nil)).Elem()
- fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
-)
-
-// indirectToStringerOrError returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
-// or error,
-func indirectToStringerOrError(a interface{}) interface{} {
- if a == nil {
- return nil
- }
- v := reflect.ValueOf(a)
- for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-// stringify converts its arguments to a string and the type of the content.
-// All pointers are dereferenced, as in the text/template package.
-func stringify(args ...interface{}) (string, contentType) {
- if len(args) == 1 {
- switch s := indirect(args[0]).(type) {
- case string:
- return s, contentTypePlain
- case CSS:
- return string(s), contentTypeCSS
- case HTML:
- return string(s), contentTypeHTML
- case HTMLAttr:
- return string(s), contentTypeHTMLAttr
- case JS:
- return string(s), contentTypeJS
- case JSStr:
- return string(s), contentTypeJSStr
- case URL:
- return string(s), contentTypeURL
- }
- }
- for i, arg := range args {
- args[i] = indirectToStringerOrError(arg)
- }
- return fmt.Sprint(args...), contentTypePlain
-}
diff --git a/src/pkg/html/template/content_test.go b/src/pkg/html/template/content_test.go
deleted file mode 100644
index 5f3ffe2d3..000000000
--- a/src/pkg/html/template/content_test.go
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "strings"
- "testing"
-)
-
-func TestTypedContent(t *testing.T) {
- data := []interface{}{
- `<b> "foo%" O'Reilly &bar;`,
- CSS(`a[href =~ "//example.com"]#foo`),
- HTML(`Hello, <b>World</b> &amp;tc!`),
- HTMLAttr(` dir="ltr"`),
- JS(`c && alert("Hello, World!");`),
- JSStr(`Hello, World & O'Reilly\x21`),
- URL(`greeting=H%69&addressee=(World)`),
- }
-
- // For each content sensitive escaper, see how it does on
- // each of the typed strings above.
- tests := []struct {
- // A template containing a single {{.}}.
- input string
- want []string
- }{
- {
- `<style>{{.}} { color: blue }</style>`,
- []string{
- `ZgotmplZ`,
- // Allowed but not escaped.
- `a[href =~ "//example.com"]#foo`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- },
- },
- {
- `<div style="{{.}}">`,
- []string{
- `ZgotmplZ`,
- // Allowed and HTML escaped.
- `a[href =~ &#34;//example.com&#34;]#foo`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- },
- },
- {
- `{{.}}`,
- []string{
- `&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
- `a[href =~ &#34;//example.com&#34;]#foo`,
- // Not escaped.
- `Hello, <b>World</b> &amp;tc!`,
- ` dir=&#34;ltr&#34;`,
- `c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
- `greeting=H%69&amp;addressee=(World)`,
- },
- },
- {
- `<a{{.}}>`,
- []string{
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- // Allowed and HTML escaped.
- ` dir="ltr"`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- `ZgotmplZ`,
- },
- },
- {
- `<a title={{.}}>`,
- []string{
- `&lt;b&gt;&#32;&#34;foo%&#34;&#32;O&#39;Reilly&#32;&amp;bar;`,
- `a[href&#32;&#61;~&#32;&#34;//example.com&#34;]#foo`,
- // Tags stripped, spaces escaped, entity not re-escaped.
- `Hello,&#32;World&#32;&amp;tc!`,
- `&#32;dir&#61;&#34;ltr&#34;`,
- `c&#32;&amp;&amp;&#32;alert(&#34;Hello,&#32;World!&#34;);`,
- `Hello,&#32;World&#32;&amp;&#32;O&#39;Reilly\x21`,
- `greeting&#61;H%69&amp;addressee&#61;(World)`,
- },
- },
- {
- `<a title='{{.}}'>`,
- []string{
- `&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
- `a[href =~ &#34;//example.com&#34;]#foo`,
- // Tags stripped, entity not re-escaped.
- `Hello, World &amp;tc!`,
- ` dir=&#34;ltr&#34;`,
- `c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
- `greeting=H%69&amp;addressee=(World)`,
- },
- },
- {
- `<textarea>{{.}}</textarea>`,
- []string{
- `&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
- `a[href =~ &#34;//example.com&#34;]#foo`,
- // Angle brackets escaped to prevent injection of close tags, entity not re-escaped.
- `Hello, &lt;b&gt;World&lt;/b&gt; &amp;tc!`,
- ` dir=&#34;ltr&#34;`,
- `c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
- `greeting=H%69&amp;addressee=(World)`,
- },
- },
- {
- `<script>alert({{.}})</script>`,
- []string{
- `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
- `"a[href =~ \"//example.com\"]#foo"`,
- `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
- `" dir=\"ltr\""`,
- // Not escaped.
- `c && alert("Hello, World!");`,
- // Escape sequence not over-escaped.
- `"Hello, World & O'Reilly\x21"`,
- `"greeting=H%69\u0026addressee=(World)"`,
- },
- },
- {
- `<button onclick="alert({{.}})">`,
- []string{
- `&#34;\u003cb\u003e \&#34;foo%\&#34; O&#39;Reilly \u0026bar;&#34;`,
- `&#34;a[href =~ \&#34;//example.com\&#34;]#foo&#34;`,
- `&#34;Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!&#34;`,
- `&#34; dir=\&#34;ltr\&#34;&#34;`,
- // Not JS escaped but HTML escaped.
- `c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- // Escape sequence not over-escaped.
- `&#34;Hello, World &amp; O&#39;Reilly\x21&#34;`,
- `&#34;greeting=H%69\u0026addressee=(World)&#34;`,
- },
- },
- {
- `<script>alert("{{.}}")</script>`,
- []string{
- `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
- `a[href =~ \x22\/\/example.com\x22]#foo`,
- `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
- ` dir=\x22ltr\x22`,
- `c \x26\x26 alert(\x22Hello, World!\x22);`,
- // Escape sequence not over-escaped.
- `Hello, World \x26 O\x27Reilly\x21`,
- `greeting=H%69\x26addressee=(World)`,
- },
- },
- {
- `<button onclick='alert("{{.}}")'>`,
- []string{
- `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
- `a[href =~ \x22\/\/example.com\x22]#foo`,
- `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
- ` dir=\x22ltr\x22`,
- `c \x26\x26 alert(\x22Hello, World!\x22);`,
- // Escape sequence not over-escaped.
- `Hello, World \x26 O\x27Reilly\x21`,
- `greeting=H%69\x26addressee=(World)`,
- },
- },
- {
- `<a href="?q={{.}}">`,
- []string{
- `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
- `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
- `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
- `%20dir%3d%22ltr%22`,
- `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
- `Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
- // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
- `greeting=H%69&amp;addressee=%28World%29`,
- },
- },
- {
- `<style>body { background: url('?img={{.}}') }</style>`,
- []string{
- `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
- `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
- `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
- `%20dir%3d%22ltr%22`,
- `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
- `Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
- // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
- `greeting=H%69&addressee=%28World%29`,
- },
- },
- }
-
- for _, test := range tests {
- tmpl := Must(New("x").Parse(test.input))
- pre := strings.Index(test.input, "{{.}}")
- post := len(test.input) - (pre + 5)
- var b bytes.Buffer
- for i, x := range data {
- b.Reset()
- if err := tmpl.Execute(&b, x); err != nil {
- t.Errorf("%q with %v: %s", test.input, x, err)
- continue
- }
- if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got {
- t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got)
- continue
- }
- }
- }
-}
-
-// Test that we print using the String method. Was issue 3073.
-type stringer struct {
- v int
-}
-
-func (s *stringer) String() string {
- return fmt.Sprintf("string=%d", s.v)
-}
-
-type errorer struct {
- v int
-}
-
-func (s *errorer) Error() string {
- return fmt.Sprintf("error=%d", s.v)
-}
-
-func TestStringer(t *testing.T) {
- s := &stringer{3}
- b := new(bytes.Buffer)
- tmpl := Must(New("x").Parse("{{.}}"))
- if err := tmpl.Execute(b, s); err != nil {
- t.Fatal(err)
- }
- var expect = "string=3"
- if b.String() != expect {
- t.Errorf("expected %q got %q", expect, b.String())
- }
- e := &errorer{7}
- b.Reset()
- if err := tmpl.Execute(b, e); err != nil {
- t.Fatal(err)
- }
- expect = "error=7"
- if b.String() != expect {
- t.Errorf("expected %q got %q", expect, b.String())
- }
-}
-
-// https://code.google.com/p/go/issues/detail?id=5982
-func TestEscapingNilNonemptyInterfaces(t *testing.T) {
- tmpl := Must(New("x").Parse("{{.E}}"))
-
- got := new(bytes.Buffer)
- testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand
- tmpl.Execute(got, testData)
-
- // Use this data instead of just hard-coding "&lt;nil&gt;" to avoid
- // dependencies on the html escaper and the behavior of fmt w.r.t. nil.
- want := new(bytes.Buffer)
- data := struct{ E string }{E: fmt.Sprint(nil)}
- tmpl.Execute(want, data)
-
- if !bytes.Equal(want.Bytes(), got.Bytes()) {
- t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes()))
- }
-}
diff --git a/src/pkg/html/template/context.go b/src/pkg/html/template/context.go
deleted file mode 100644
index 59e794d68..000000000
--- a/src/pkg/html/template/context.go
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
-)
-
-// context describes the state an HTML parser must be in when it reaches the
-// portion of HTML produced by evaluating a particular template node.
-//
-// The zero value of type context is the start context for a template that
-// produces an HTML fragment as defined at
-// http://www.w3.org/TR/html5/syntax.html#the-end
-// where the context element is null.
-type context struct {
- state state
- delim delim
- urlPart urlPart
- jsCtx jsCtx
- attr attr
- element element
- err *Error
-}
-
-func (c context) String() string {
- return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, c.err)
-}
-
-// eq reports whether two contexts are equal.
-func (c context) eq(d context) bool {
- return c.state == d.state &&
- c.delim == d.delim &&
- c.urlPart == d.urlPart &&
- c.jsCtx == d.jsCtx &&
- c.attr == d.attr &&
- c.element == d.element &&
- c.err == d.err
-}
-
-// mangle produces an identifier that includes a suffix that distinguishes it
-// from template names mangled with different contexts.
-func (c context) mangle(templateName string) string {
- // The mangled name for the default context is the input templateName.
- if c.state == stateText {
- return templateName
- }
- s := templateName + "$htmltemplate_" + c.state.String()
- if c.delim != 0 {
- s += "_" + c.delim.String()
- }
- if c.urlPart != 0 {
- s += "_" + c.urlPart.String()
- }
- if c.jsCtx != 0 {
- s += "_" + c.jsCtx.String()
- }
- if c.attr != 0 {
- s += "_" + c.attr.String()
- }
- if c.element != 0 {
- s += "_" + c.element.String()
- }
- return s
-}
-
-// state describes a high-level HTML parser state.
-//
-// It bounds the top of the element stack, and by extension the HTML insertion
-// mode, but also contains state that does not correspond to anything in the
-// HTML5 parsing algorithm because a single token production in the HTML
-// grammar may contain embedded actions in a template. For instance, the quoted
-// HTML attribute produced by
-// <div title="Hello {{.World}}">
-// is a single token in HTML's grammar but in a template spans several nodes.
-type state uint8
-
-const (
- // stateText is parsed character data. An HTML parser is in
- // this state when its parse position is outside an HTML tag,
- // directive, comment, and special element body.
- stateText state = iota
- // stateTag occurs before an HTML attribute or the end of a tag.
- stateTag
- // stateAttrName occurs inside an attribute name.
- // It occurs between the ^'s in ` ^name^ = value`.
- stateAttrName
- // stateAfterName occurs after an attr name has ended but before any
- // equals sign. It occurs between the ^'s in ` name^ ^= value`.
- stateAfterName
- // stateBeforeValue occurs after the equals sign but before the value.
- // It occurs between the ^'s in ` name =^ ^value`.
- stateBeforeValue
- // stateHTMLCmt occurs inside an <!-- HTML comment -->.
- stateHTMLCmt
- // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>)
- // as described at http://www.w3.org/TR/html5/syntax.html#elements-0
- stateRCDATA
- // stateAttr occurs inside an HTML attribute whose content is text.
- stateAttr
- // stateURL occurs inside an HTML attribute whose content is a URL.
- stateURL
- // stateJS occurs inside an event handler or script element.
- stateJS
- // stateJSDqStr occurs inside a JavaScript double quoted string.
- stateJSDqStr
- // stateJSSqStr occurs inside a JavaScript single quoted string.
- stateJSSqStr
- // stateJSRegexp occurs inside a JavaScript regexp literal.
- stateJSRegexp
- // stateJSBlockCmt occurs inside a JavaScript /* block comment */.
- stateJSBlockCmt
- // stateJSLineCmt occurs inside a JavaScript // line comment.
- stateJSLineCmt
- // stateCSS occurs inside a <style> element or style attribute.
- stateCSS
- // stateCSSDqStr occurs inside a CSS double quoted string.
- stateCSSDqStr
- // stateCSSSqStr occurs inside a CSS single quoted string.
- stateCSSSqStr
- // stateCSSDqURL occurs inside a CSS double quoted url("...").
- stateCSSDqURL
- // stateCSSSqURL occurs inside a CSS single quoted url('...').
- stateCSSSqURL
- // stateCSSURL occurs inside a CSS unquoted url(...).
- stateCSSURL
- // stateCSSBlockCmt occurs inside a CSS /* block comment */.
- stateCSSBlockCmt
- // stateCSSLineCmt occurs inside a CSS // line comment.
- stateCSSLineCmt
- // stateError is an infectious error state outside any valid
- // HTML/CSS/JS construct.
- stateError
-)
-
-var stateNames = [...]string{
- stateText: "stateText",
- stateTag: "stateTag",
- stateAttrName: "stateAttrName",
- stateAfterName: "stateAfterName",
- stateBeforeValue: "stateBeforeValue",
- stateHTMLCmt: "stateHTMLCmt",
- stateRCDATA: "stateRCDATA",
- stateAttr: "stateAttr",
- stateURL: "stateURL",
- stateJS: "stateJS",
- stateJSDqStr: "stateJSDqStr",
- stateJSSqStr: "stateJSSqStr",
- stateJSRegexp: "stateJSRegexp",
- stateJSBlockCmt: "stateJSBlockCmt",
- stateJSLineCmt: "stateJSLineCmt",
- stateCSS: "stateCSS",
- stateCSSDqStr: "stateCSSDqStr",
- stateCSSSqStr: "stateCSSSqStr",
- stateCSSDqURL: "stateCSSDqURL",
- stateCSSSqURL: "stateCSSSqURL",
- stateCSSURL: "stateCSSURL",
- stateCSSBlockCmt: "stateCSSBlockCmt",
- stateCSSLineCmt: "stateCSSLineCmt",
- stateError: "stateError",
-}
-
-func (s state) String() string {
- if int(s) < len(stateNames) {
- return stateNames[s]
- }
- return fmt.Sprintf("illegal state %d", int(s))
-}
-
-// isComment is true for any state that contains content meant for template
-// authors & maintainers, not for end-users or machines.
-func isComment(s state) bool {
- switch s {
- case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
- return true
- }
- return false
-}
-
-// isInTag return whether s occurs solely inside an HTML tag.
-func isInTag(s state) bool {
- switch s {
- case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr:
- return true
- }
- return false
-}
-
-// delim is the delimiter that will end the current HTML attribute.
-type delim uint8
-
-const (
- // delimNone occurs outside any attribute.
- delimNone delim = iota
- // delimDoubleQuote occurs when a double quote (") closes the attribute.
- delimDoubleQuote
- // delimSingleQuote occurs when a single quote (') closes the attribute.
- delimSingleQuote
- // delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
- // closes the attribute.
- delimSpaceOrTagEnd
-)
-
-var delimNames = [...]string{
- delimNone: "delimNone",
- delimDoubleQuote: "delimDoubleQuote",
- delimSingleQuote: "delimSingleQuote",
- delimSpaceOrTagEnd: "delimSpaceOrTagEnd",
-}
-
-func (d delim) String() string {
- if int(d) < len(delimNames) {
- return delimNames[d]
- }
- return fmt.Sprintf("illegal delim %d", int(d))
-}
-
-// urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
-// encoding strategies.
-type urlPart uint8
-
-const (
- // urlPartNone occurs when not in a URL, or possibly at the start:
- // ^ in "^http://auth/path?k=v#frag".
- urlPartNone urlPart = iota
- // urlPartPreQuery occurs in the scheme, authority, or path; between the
- // ^s in "h^ttp://auth/path^?k=v#frag".
- urlPartPreQuery
- // urlPartQueryOrFrag occurs in the query portion between the ^s in
- // "http://auth/path?^k=v#frag^".
- urlPartQueryOrFrag
- // urlPartUnknown occurs due to joining of contexts both before and
- // after the query separator.
- urlPartUnknown
-)
-
-var urlPartNames = [...]string{
- urlPartNone: "urlPartNone",
- urlPartPreQuery: "urlPartPreQuery",
- urlPartQueryOrFrag: "urlPartQueryOrFrag",
- urlPartUnknown: "urlPartUnknown",
-}
-
-func (u urlPart) String() string {
- if int(u) < len(urlPartNames) {
- return urlPartNames[u]
- }
- return fmt.Sprintf("illegal urlPart %d", int(u))
-}
-
-// jsCtx determines whether a '/' starts a regular expression literal or a
-// division operator.
-type jsCtx uint8
-
-const (
- // jsCtxRegexp occurs where a '/' would start a regexp literal.
- jsCtxRegexp jsCtx = iota
- // jsCtxDivOp occurs where a '/' would start a division operator.
- jsCtxDivOp
- // jsCtxUnknown occurs where a '/' is ambiguous due to context joining.
- jsCtxUnknown
-)
-
-func (c jsCtx) String() string {
- switch c {
- case jsCtxRegexp:
- return "jsCtxRegexp"
- case jsCtxDivOp:
- return "jsCtxDivOp"
- case jsCtxUnknown:
- return "jsCtxUnknown"
- }
- return fmt.Sprintf("illegal jsCtx %d", int(c))
-}
-
-// element identifies the HTML element when inside a start tag or special body.
-// Certain HTML element (for example <script> and <style>) have bodies that are
-// treated differently from stateText so the element type is necessary to
-// transition into the correct context at the end of a tag and to identify the
-// end delimiter for the body.
-type element uint8
-
-const (
- // elementNone occurs outside a special tag or special element body.
- elementNone element = iota
- // elementScript corresponds to the raw text <script> element.
- elementScript
- // elementStyle corresponds to the raw text <style> element.
- elementStyle
- // elementTextarea corresponds to the RCDATA <textarea> element.
- elementTextarea
- // elementTitle corresponds to the RCDATA <title> element.
- elementTitle
-)
-
-var elementNames = [...]string{
- elementNone: "elementNone",
- elementScript: "elementScript",
- elementStyle: "elementStyle",
- elementTextarea: "elementTextarea",
- elementTitle: "elementTitle",
-}
-
-func (e element) String() string {
- if int(e) < len(elementNames) {
- return elementNames[e]
- }
- return fmt.Sprintf("illegal element %d", int(e))
-}
-
-// attr identifies the most recent HTML attribute when inside a start tag.
-type attr uint8
-
-const (
- // attrNone corresponds to a normal attribute or no attribute.
- attrNone attr = iota
- // attrScript corresponds to an event handler attribute.
- attrScript
- // attrStyle corresponds to the style attribute whose value is CSS.
- attrStyle
- // attrURL corresponds to an attribute whose value is a URL.
- attrURL
-)
-
-var attrNames = [...]string{
- attrNone: "attrNone",
- attrScript: "attrScript",
- attrStyle: "attrStyle",
- attrURL: "attrURL",
-}
-
-func (a attr) String() string {
- if int(a) < len(attrNames) {
- return attrNames[a]
- }
- return fmt.Sprintf("illegal attr %d", int(a))
-}
diff --git a/src/pkg/html/template/css.go b/src/pkg/html/template/css.go
deleted file mode 100644
index 634f183f7..000000000
--- a/src/pkg/html/template/css.go
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "unicode"
- "unicode/utf8"
-)
-
-// endsWithCSSKeyword reports whether b ends with an ident that
-// case-insensitively matches the lower-case kw.
-func endsWithCSSKeyword(b []byte, kw string) bool {
- i := len(b) - len(kw)
- if i < 0 {
- // Too short.
- return false
- }
- if i != 0 {
- r, _ := utf8.DecodeLastRune(b[:i])
- if isCSSNmchar(r) {
- // Too long.
- return false
- }
- }
- // Many CSS keywords, such as "!important" can have characters encoded,
- // but the URI production does not allow that according to
- // http://www.w3.org/TR/css3-syntax/#TOK-URI
- // This does not attempt to recognize encoded keywords. For example,
- // given "\75\72\6c" and "url" this return false.
- return string(bytes.ToLower(b[i:])) == kw
-}
-
-// isCSSNmchar reports whether rune is allowed anywhere in a CSS identifier.
-func isCSSNmchar(r rune) bool {
- // Based on the CSS3 nmchar production but ignores multi-rune escape
- // sequences.
- // http://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar
- return 'a' <= r && r <= 'z' ||
- 'A' <= r && r <= 'Z' ||
- '0' <= r && r <= '9' ||
- r == '-' ||
- r == '_' ||
- // Non-ASCII cases below.
- 0x80 <= r && r <= 0xd7ff ||
- 0xe000 <= r && r <= 0xfffd ||
- 0x10000 <= r && r <= 0x10ffff
-}
-
-// decodeCSS decodes CSS3 escapes given a sequence of stringchars.
-// If there is no change, it returns the input, otherwise it returns a slice
-// backed by a new array.
-// http://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar.
-func decodeCSS(s []byte) []byte {
- i := bytes.IndexByte(s, '\\')
- if i == -1 {
- return s
- }
- // The UTF-8 sequence for a codepoint is never longer than 1 + the
- // number hex digits need to represent that codepoint, so len(s) is an
- // upper bound on the output length.
- b := make([]byte, 0, len(s))
- for len(s) != 0 {
- i := bytes.IndexByte(s, '\\')
- if i == -1 {
- i = len(s)
- }
- b, s = append(b, s[:i]...), s[i:]
- if len(s) < 2 {
- break
- }
- // http://www.w3.org/TR/css3-syntax/#SUBTOK-escape
- // escape ::= unicode | '\' [#x20-#x7E#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF]
- if isHex(s[1]) {
- // http://www.w3.org/TR/css3-syntax/#SUBTOK-unicode
- // unicode ::= '\' [0-9a-fA-F]{1,6} wc?
- j := 2
- for j < len(s) && j < 7 && isHex(s[j]) {
- j++
- }
- r := hexDecode(s[1:j])
- if r > unicode.MaxRune {
- r, j = r/16, j-1
- }
- n := utf8.EncodeRune(b[len(b):cap(b)], r)
- // The optional space at the end allows a hex
- // sequence to be followed by a literal hex.
- // string(decodeCSS([]byte(`\A B`))) == "\nB"
- b, s = b[:len(b)+n], skipCSSSpace(s[j:])
- } else {
- // `\\` decodes to `\` and `\"` to `"`.
- _, n := utf8.DecodeRune(s[1:])
- b, s = append(b, s[1:1+n]...), s[1+n:]
- }
- }
- return b
-}
-
-// isHex reports whether the given character is a hex digit.
-func isHex(c byte) bool {
- return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
-}
-
-// hexDecode decodes a short hex digit sequence: "10" -> 16.
-func hexDecode(s []byte) rune {
- n := '\x00'
- for _, c := range s {
- n <<= 4
- switch {
- case '0' <= c && c <= '9':
- n |= rune(c - '0')
- case 'a' <= c && c <= 'f':
- n |= rune(c-'a') + 10
- case 'A' <= c && c <= 'F':
- n |= rune(c-'A') + 10
- default:
- panic(fmt.Sprintf("Bad hex digit in %q", s))
- }
- }
- return n
-}
-
-// skipCSSSpace returns a suffix of c, skipping over a single space.
-func skipCSSSpace(c []byte) []byte {
- if len(c) == 0 {
- return c
- }
- // wc ::= #x9 | #xA | #xC | #xD | #x20
- switch c[0] {
- case '\t', '\n', '\f', ' ':
- return c[1:]
- case '\r':
- // This differs from CSS3's wc production because it contains a
- // probable spec error whereby wc contains all the single byte
- // sequences in nl (newline) but not CRLF.
- if len(c) >= 2 && c[1] == '\n' {
- return c[2:]
- }
- return c[1:]
- }
- return c
-}
-
-// isCSSSpace reports whether b is a CSS space char as defined in wc.
-func isCSSSpace(b byte) bool {
- switch b {
- case '\t', '\n', '\f', '\r', ' ':
- return true
- }
- return false
-}
-
-// cssEscaper escapes HTML and CSS special characters using \<hex>+ escapes.
-func cssEscaper(args ...interface{}) string {
- s, _ := stringify(args...)
- var b bytes.Buffer
- written := 0
- for i, r := range s {
- var repl string
- switch r {
- case 0:
- repl = `\0`
- case '\t':
- repl = `\9`
- case '\n':
- repl = `\a`
- case '\f':
- repl = `\c`
- case '\r':
- repl = `\d`
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- case '"':
- repl = `\22`
- case '&':
- repl = `\26`
- case '\'':
- repl = `\27`
- case '(':
- repl = `\28`
- case ')':
- repl = `\29`
- case '+':
- repl = `\2b`
- case '/':
- repl = `\2f`
- case ':':
- repl = `\3a`
- case ';':
- repl = `\3b`
- case '<':
- repl = `\3c`
- case '>':
- repl = `\3e`
- case '\\':
- repl = `\\`
- case '{':
- repl = `\7b`
- case '}':
- repl = `\7d`
- default:
- continue
- }
- b.WriteString(s[written:i])
- b.WriteString(repl)
- written = i + utf8.RuneLen(r)
- if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
- b.WriteByte(' ')
- }
- }
- if written == 0 {
- return s
- }
- b.WriteString(s[written:])
- return b.String()
-}
-
-var expressionBytes = []byte("expression")
-var mozBindingBytes = []byte("mozbinding")
-
-// cssValueFilter allows innocuous CSS values in the output including CSS
-// quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values
-// (inherit, blue), and colors (#888).
-// It filters out unsafe values, such as those that affect token boundaries,
-// and anything that might execute scripts.
-func cssValueFilter(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeCSS {
- return s
- }
- b, id := decodeCSS([]byte(s)), make([]byte, 0, 64)
-
- // CSS3 error handling is specified as honoring string boundaries per
- // http://www.w3.org/TR/css3-syntax/#error-handling :
- // Malformed declarations. User agents must handle unexpected
- // tokens encountered while parsing a declaration by reading until
- // the end of the declaration, while observing the rules for
- // matching pairs of (), [], {}, "", and '', and correctly handling
- // escapes. For example, a malformed declaration may be missing a
- // property, colon (:) or value.
- // So we need to make sure that values do not have mismatched bracket
- // or quote characters to prevent the browser from restarting parsing
- // inside a string that might embed JavaScript source.
- for i, c := range b {
- switch c {
- case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}':
- return filterFailsafe
- case '-':
- // Disallow <!-- or -->.
- // -- should not appear in valid identifiers.
- if i != 0 && b[i-1] == '-' {
- return filterFailsafe
- }
- default:
- if c < 0x80 && isCSSNmchar(rune(c)) {
- id = append(id, c)
- }
- }
- }
- id = bytes.ToLower(id)
- if bytes.Index(id, expressionBytes) != -1 || bytes.Index(id, mozBindingBytes) != -1 {
- return filterFailsafe
- }
- return string(b)
-}
diff --git a/src/pkg/html/template/css_test.go b/src/pkg/html/template/css_test.go
deleted file mode 100644
index a735638b0..000000000
--- a/src/pkg/html/template/css_test.go
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "strconv"
- "strings"
- "testing"
-)
-
-func TestEndsWithCSSKeyword(t *testing.T) {
- tests := []struct {
- css, kw string
- want bool
- }{
- {"", "url", false},
- {"url", "url", true},
- {"URL", "url", true},
- {"Url", "url", true},
- {"url", "important", false},
- {"important", "important", true},
- {"image-url", "url", false},
- {"imageurl", "url", false},
- {"image url", "url", true},
- }
- for _, test := range tests {
- got := endsWithCSSKeyword([]byte(test.css), test.kw)
- if got != test.want {
- t.Errorf("want %t but got %t for css=%v, kw=%v", test.want, got, test.css, test.kw)
- }
- }
-}
-
-func TestIsCSSNmchar(t *testing.T) {
- tests := []struct {
- rune rune
- want bool
- }{
- {0, false},
- {'0', true},
- {'9', true},
- {'A', true},
- {'Z', true},
- {'a', true},
- {'z', true},
- {'_', true},
- {'-', true},
- {':', false},
- {';', false},
- {' ', false},
- {0x7f, false},
- {0x80, true},
- {0x1234, true},
- {0xd800, false},
- {0xdc00, false},
- {0xfffe, false},
- {0x10000, true},
- {0x110000, false},
- }
- for _, test := range tests {
- got := isCSSNmchar(test.rune)
- if got != test.want {
- t.Errorf("%q: want %t but got %t", string(test.rune), test.want, got)
- }
- }
-}
-
-func TestDecodeCSS(t *testing.T) {
- tests := []struct {
- css, want string
- }{
- {``, ``},
- {`foo`, `foo`},
- {`foo\`, `foo`},
- {`foo\\`, `foo\`},
- {`\`, ``},
- {`\A`, "\n"},
- {`\a`, "\n"},
- {`\0a`, "\n"},
- {`\00000a`, "\n"},
- {`\000000a`, "\u0000a"},
- {`\1234 5`, "\u1234" + "5"},
- {`\1234\20 5`, "\u1234" + " 5"},
- {`\1234\A 5`, "\u1234" + "\n5"},
- {"\\1234\t5", "\u1234" + "5"},
- {"\\1234\n5", "\u1234" + "5"},
- {"\\1234\r\n5", "\u1234" + "5"},
- {`\12345`, "\U00012345"},
- {`\\`, `\`},
- {`\\ `, `\ `},
- {`\"`, `"`},
- {`\'`, `'`},
- {`\.`, `.`},
- {`\. .`, `. .`},
- {
- `The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3e dog\3c/canine\3e`,
- "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>",
- },
- }
- for _, test := range tests {
- got1 := string(decodeCSS([]byte(test.css)))
- if got1 != test.want {
- t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.css, test.want, got1)
- }
- recoded := cssEscaper(got1)
- if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want {
- t.Errorf("%q: escape & decode not dual for %q", test.css, recoded)
- }
- }
-}
-
-func TestHexDecode(t *testing.T) {
- for i := 0; i < 0x200000; i += 101 /* coprime with 16 */ {
- s := strconv.FormatInt(int64(i), 16)
- if got := int(hexDecode([]byte(s))); got != i {
- t.Errorf("%s: want %d but got %d", s, i, got)
- }
- s = strings.ToUpper(s)
- if got := int(hexDecode([]byte(s))); got != i {
- t.Errorf("%s: want %d but got %d", s, i, got)
- }
- }
-}
-
-func TestSkipCSSSpace(t *testing.T) {
- tests := []struct {
- css, want string
- }{
- {"", ""},
- {"foo", "foo"},
- {"\n", ""},
- {"\r\n", ""},
- {"\r", ""},
- {"\t", ""},
- {" ", ""},
- {"\f", ""},
- {" foo", "foo"},
- {" foo", " foo"},
- {`\20`, `\20`},
- }
- for _, test := range tests {
- got := string(skipCSSSpace([]byte(test.css)))
- if got != test.want {
- t.Errorf("%q: want %q but got %q", test.css, test.want, got)
- }
- }
-}
-
-func TestCSSEscaper(t *testing.T) {
- input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !"#$%&'()*+,-./` +
- `0123456789:;<=>?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
-
- want := ("\\0\x01\x02\x03\x04\x05\x06\x07" +
- "\x08\\9 \\a\x0b\\c \\d\x0E\x0F" +
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !\22#$%\26\27\28\29*\2b,-.\2f ` +
- `0123456789\3a\3b\3c=\3e?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\\]^_` +
- "`abcdefghijklmno" +
- `pqrstuvwxyz\7b|\7d~` + "\u007f" +
- "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
-
- got := cssEscaper(input)
- if got != want {
- t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got)
- }
-
- got = string(decodeCSS([]byte(got)))
- if input != got {
- t.Errorf("decode: want\n\t%q\nbut got\n\t%q", input, got)
- }
-}
-
-func TestCSSValueFilter(t *testing.T) {
- tests := []struct {
- css, want string
- }{
- {"", ""},
- {"foo", "foo"},
- {"0", "0"},
- {"0px", "0px"},
- {"-5px", "-5px"},
- {"1.25in", "1.25in"},
- {"+.33em", "+.33em"},
- {"100%", "100%"},
- {"12.5%", "12.5%"},
- {".foo", ".foo"},
- {"#bar", "#bar"},
- {"corner-radius", "corner-radius"},
- {"-moz-corner-radius", "-moz-corner-radius"},
- {"#000", "#000"},
- {"#48f", "#48f"},
- {"#123456", "#123456"},
- {"U+00-FF, U+980-9FF", "U+00-FF, U+980-9FF"},
- {"color: red", "color: red"},
- {"<!--", "ZgotmplZ"},
- {"-->", "ZgotmplZ"},
- {"<![CDATA[", "ZgotmplZ"},
- {"]]>", "ZgotmplZ"},
- {"</style", "ZgotmplZ"},
- {`"`, "ZgotmplZ"},
- {`'`, "ZgotmplZ"},
- {"`", "ZgotmplZ"},
- {"\x00", "ZgotmplZ"},
- {"/* foo */", "ZgotmplZ"},
- {"//", "ZgotmplZ"},
- {"[href=~", "ZgotmplZ"},
- {"expression(alert(1337))", "ZgotmplZ"},
- {"-expression(alert(1337))", "ZgotmplZ"},
- {"expression", "ZgotmplZ"},
- {"Expression", "ZgotmplZ"},
- {"EXPRESSION", "ZgotmplZ"},
- {"-moz-binding", "ZgotmplZ"},
- {"-expr\x00ession(alert(1337))", "ZgotmplZ"},
- {`-expr\0ession(alert(1337))`, "ZgotmplZ"},
- {`-express\69on(alert(1337))`, "ZgotmplZ"},
- {`-express\69 on(alert(1337))`, "ZgotmplZ"},
- {`-exp\72 ession(alert(1337))`, "ZgotmplZ"},
- {`-exp\52 ession(alert(1337))`, "ZgotmplZ"},
- {`-exp\000052 ession(alert(1337))`, "ZgotmplZ"},
- {`-expre\0000073sion`, "-expre\x073sion"},
- {`@import url evil.css`, "ZgotmplZ"},
- }
- for _, test := range tests {
- got := cssValueFilter(test.css)
- if got != test.want {
- t.Errorf("%q: want %q but got %q", test.css, test.want, got)
- }
- }
-}
-
-func BenchmarkCSSEscaper(b *testing.B) {
- for i := 0; i < b.N; i++ {
- cssEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
-
-func BenchmarkCSSEscaperNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- cssEscaper("The quick, brown fox jumps over the lazy dog.")
- }
-}
-
-func BenchmarkDecodeCSS(b *testing.B) {
- s := []byte(`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3edog\3c/canine\3e`)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- decodeCSS(s)
- }
-}
-
-func BenchmarkDecodeCSSNoSpecials(b *testing.B) {
- s := []byte("The quick, brown fox jumps over the lazy dog.")
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- decodeCSS(s)
- }
-}
-
-func BenchmarkCSSValueFilter(b *testing.B) {
- for i := 0; i < b.N; i++ {
- cssValueFilter(` e\78preS\0Sio/**/n(alert(1337))`)
- }
-}
-
-func BenchmarkCSSValueFilterOk(b *testing.B) {
- for i := 0; i < b.N; i++ {
- cssValueFilter(`Times New Roman`)
- }
-}
diff --git a/src/pkg/html/template/doc.go b/src/pkg/html/template/doc.go
deleted file mode 100644
index d422ada37..000000000
--- a/src/pkg/html/template/doc.go
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-Package template (html/template) implements data-driven templates for
-generating HTML output safe against code injection. It provides the
-same interface as package text/template and should be used instead of
-text/template whenever the output is HTML.
-
-The documentation here focuses on the security features of the package.
-For information about how to program the templates themselves, see the
-documentation for text/template.
-
-Introduction
-
-This package wraps package text/template so you can share its template API
-to parse and execute HTML templates safely.
-
- tmpl, err := template.New("name").Parse(...)
- // Error checking elided
- err = tmpl.Execute(out, data)
-
-If successful, tmpl will now be injection-safe. Otherwise, err is an error
-defined in the docs for ErrorCode.
-
-HTML templates treat data values as plain text which should be encoded so they
-can be safely embedded in an HTML document. The escaping is contextual, so
-actions can appear within JavaScript, CSS, and URI contexts.
-
-The security model used by this package assumes that template authors are
-trusted, while Execute's data parameter is not. More details are
-provided below.
-
-Example
-
- import "text/template"
- ...
- t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
- err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
-
-produces
-
- Hello, <script>alert('you have been pwned')</script>!
-
-but the contextual autoescaping in html/template
-
- import "html/template"
- ...
- t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
- err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
-
-produces safe, escaped HTML output
-
- Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
-
-
-Contexts
-
-This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing
-functions to each simple action pipeline, so given the excerpt
-
- <a href="/search?q={{.}}">{{.}}</a>
-
-At parse time each {{.}} is overwritten to add escaping functions as necessary.
-In this case it becomes
-
- <a href="/search?q={{. | urlquery}}">{{. | html}}</a>
-
-
-Errors
-
-See the documentation of ErrorCode for details.
-
-
-A fuller picture
-
-The rest of this package comment may be skipped on first reading; it includes
-details necessary to understand escaping contexts and error messages. Most users
-will not need to understand these details.
-
-
-Contexts
-
-Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows
-how {{.}} appears when used in the context to the left.
-
- Context {{.}} After
- {{.}} O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
- <a title='{{.}}'> O&#39;Reilly: How are you?
- <a href="/{{.}}"> O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
- <a href="?q={{.}}"> O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
- <a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...?
- <a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?"
- <a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f
-
-If used in an unsafe context, then the value might be filtered out:
-
- Context {{.}} After
- <a href="{{.}}"> #ZgotmplZ
-
-since "O'Reilly:" is not an allowed protocol like "http:".
-
-
-If {{.}} is the innocuous word, `left`, then it can appear more widely,
-
- Context {{.}} After
- {{.}} left
- <a title='{{.}}'> left
- <a href='{{.}}'> left
- <a href='/{{.}}'> left
- <a href='?dir={{.}}'> left
- <a style="border-{{.}}: 4px"> left
- <a style="align: {{.}}"> left
- <a style="background: '{{.}}'> left
- <a style="background: url('{{.}}')> left
- <style>p.{{.}} {color:red}</style> left
-
-Non-string values can be used in JavaScript contexts.
-If {{.}} is
-
- struct{A,B string}{ "foo", "bar" }
-
-in the escaped template
-
- <script>var pair = {{.}};</script>
-
-then the template output is
-
- <script>var pair = {"A": "foo", "B": "bar"};</script>
-
-See package json to understand how non-string content is marshalled for
-embedding in JavaScript contexts.
-
-
-Typed Strings
-
-By default, this package assumes that all pipelines produce a plain text string.
-It adds escaping pipeline stages necessary to correctly and safely embed that
-plain text string in the appropriate context.
-
-When a data value is not plain text, you can make sure it is not over-escaped
-by marking it with its type.
-
-Types HTML, JS, URL, and others from content.go can carry safe content that is
-exempted from escaping.
-
-The template
-
- Hello, {{.}}!
-
-can be invoked with
-
- tmpl.Execute(out, HTML(`<b>World</b>`))
-
-to produce
-
- Hello, <b>World</b>!
-
-instead of the
-
- Hello, &lt;b&gt;World&lt;b&gt;!
-
-that would have been produced if {{.}} was a regular string.
-
-
-Security Model
-
-http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
-
-This package assumes that template authors are trusted, that Execute's data
-parameter is not, and seeks to preserve the properties below in the face
-of untrusted data:
-
-Structure Preservation Property:
-"... when a template author writes an HTML tag in a safe templating language,
-the browser will interpret the corresponding portion of the output as a tag
-regardless of the values of untrusted data, and similarly for other structures
-such as attribute boundaries and JS and CSS string boundaries."
-
-Code Effect Property:
-"... only code specified by the template author should run as a result of
-injecting the template output into a page and all code specified by the
-template author should run as a result of the same."
-
-Least Surprise Property:
-"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who
-knows that contextual autoescaping happens should be able to look at a {{.}}
-and correctly infer what sanitization happens."
-*/
-package template
diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go
deleted file mode 100644
index 46e49ccf8..000000000
--- a/src/pkg/html/template/error.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
-)
-
-// Error describes a problem encountered during template Escaping.
-type Error struct {
- // ErrorCode describes the kind of error.
- ErrorCode ErrorCode
- // Name is the name of the template in which the error was encountered.
- Name string
- // Line is the line number of the error in the template source or 0.
- Line int
- // Description is a human-readable description of the problem.
- Description string
-}
-
-// ErrorCode is a code for a kind of error.
-type ErrorCode int
-
-// We define codes for each error that manifests while escaping templates, but
-// escaped templates may also fail at runtime.
-//
-// Output: "ZgotmplZ"
-// Example:
-// <img src="{{.X}}">
-// where {{.X}} evaluates to `javascript:...`
-// Discussion:
-// "ZgotmplZ" is a special value that indicates that unsafe content reached a
-// CSS or URL context at runtime. The output of the example will be
-// <img src="#ZgotmplZ">
-// If the data comes from a trusted source, use content types to exempt it
-// from filtering: URL(`javascript:...`).
-const (
- // OK indicates the lack of an error.
- OK ErrorCode = iota
-
- // ErrAmbigContext: "... appears in an ambiguous URL context"
- // Example:
- // <a href="
- // {{if .C}}
- // /path/
- // {{else}}
- // /search?q=
- // {{end}}
- // {{.X}}
- // ">
- // Discussion:
- // {{.X}} is in an ambiguous URL context since, depending on {{.C}},
- // it may be either a URL suffix or a query parameter.
- // Moving {{.X}} into the condition removes the ambiguity:
- // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}">
- ErrAmbigContext
-
- // ErrBadHTML: "expected space, attr name, or end of tag, but got ...",
- // "... in unquoted attr", "... in attribute name"
- // Example:
- // <a href = /search?q=foo>
- // <href=foo>
- // <form na<e=...>
- // <option selected<
- // Discussion:
- // This is often due to a typo in an HTML element, but some runes
- // are banned in tag names, attribute names, and unquoted attribute
- // values because they can tickle parser ambiguities.
- // Quoting all attributes is the best policy.
- ErrBadHTML
-
- // ErrBranchEnd: "{{if}} branches end in different contexts"
- // Example:
- // {{if .C}}<a href="{{end}}{{.X}}
- // Discussion:
- // Package html/template statically examines each path through an
- // {{if}}, {{range}}, or {{with}} to escape any following pipelines.
- // The example is ambiguous since {{.X}} might be an HTML text node,
- // or a URL prefix in an HTML attribute. The context of {{.X}} is
- // used to figure out how to escape it, but that context depends on
- // the run-time value of {{.C}} which is not statically known.
- //
- // The problem is usually something like missing quotes or angle
- // brackets, or can be avoided by refactoring to put the two contexts
- // into different branches of an if, range or with. If the problem
- // is in a {{range}} over a collection that should never be empty,
- // adding a dummy {{else}} can help.
- ErrBranchEnd
-
- // ErrEndContext: "... ends in a non-text context: ..."
- // Examples:
- // <div
- // <div title="no close quote>
- // <script>f()
- // Discussion:
- // Executed templates should produce a DocumentFragment of HTML.
- // Templates that end without closing tags will trigger this error.
- // Templates that should not be used in an HTML context or that
- // produce incomplete Fragments should not be executed directly.
- //
- // {{define "main"}} <script>{{template "helper"}}</script> {{end}}
- // {{define "helper"}} document.write(' <div title=" ') {{end}}
- //
- // "helper" does not produce a valid document fragment, so should
- // not be Executed directly.
- ErrEndContext
-
- // ErrNoSuchTemplate: "no such template ..."
- // Examples:
- // {{define "main"}}<div {{template "attrs"}}>{{end}}
- // {{define "attrs"}}href="{{.URL}}"{{end}}
- // Discussion:
- // Package html/template looks through template calls to compute the
- // context.
- // Here the {{.URL}} in "attrs" must be treated as a URL when called
- // from "main", but you will get this error if "attrs" is not defined
- // when "main" is parsed.
- ErrNoSuchTemplate
-
- // ErrOutputContext: "cannot compute output context for template ..."
- // Examples:
- // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}}
- // Discussion:
- // A recursive template does not end in the same context in which it
- // starts, and a reliable output context cannot be computed.
- // Look for typos in the named template.
- // If the template should not be called in the named start context,
- // look for calls to that template in unexpected contexts.
- // Maybe refactor recursive templates to not be recursive.
- ErrOutputContext
-
- // ErrPartialCharset: "unfinished JS regexp charset in ..."
- // Example:
- // <script>var pattern = /foo[{{.Chars}}]/</script>
- // Discussion:
- // Package html/template does not support interpolation into regular
- // expression literal character sets.
- ErrPartialCharset
-
- // ErrPartialEscape: "unfinished escape sequence in ..."
- // Example:
- // <script>alert("\{{.X}}")</script>
- // Discussion:
- // Package html/template does not support actions following a
- // backslash.
- // This is usually an error and there are better solutions; for
- // example
- // <script>alert("{{.X}}")</script>
- // should work, and if {{.X}} is a partial escape sequence such as
- // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`)
- ErrPartialEscape
-
- // ErrRangeLoopReentry: "on range loop re-entry: ..."
- // Example:
- // <script>var x = [{{range .}}'{{.}},{{end}}]</script>
- // Discussion:
- // If an iteration through a range would cause it to end in a
- // different context than an earlier pass, there is no single context.
- // In the example, there is missing a quote, so it is not clear
- // whether {{.}} is meant to be inside a JS string or in a JS value
- // context. The second iteration would produce something like
- //
- // <script>var x = ['firstValue,'secondValue]</script>
- ErrRangeLoopReentry
-
- // ErrSlashAmbig: '/' could start a division or regexp.
- // Example:
- // <script>
- // {{if .C}}var x = 1{{end}}
- // /-{{.N}}/i.test(x) ? doThis : doThat();
- // </script>
- // Discussion:
- // The example above could produce `var x = 1/-2/i.test(s)...`
- // in which the first '/' is a mathematical division operator or it
- // could produce `/-2/i.test(s)` in which the first '/' starts a
- // regexp literal.
- // Look for missing semicolons inside branches, and maybe add
- // parentheses to make it clear which interpretation you intend.
- ErrSlashAmbig
-)
-
-func (e *Error) Error() string {
- if e.Line != 0 {
- return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
- } else if e.Name != "" {
- return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
- }
- return "html/template: " + e.Description
-}
-
-// errorf creates an error given a format string f and args.
-// The template Name still needs to be supplied.
-func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error {
- return &Error{k, "", line, fmt.Sprintf(f, args...)}
-}
diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go
deleted file mode 100644
index 4e379828d..000000000
--- a/src/pkg/html/template/escape.go
+++ /dev/null
@@ -1,815 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "html"
- "io"
- "text/template"
- "text/template/parse"
-)
-
-// escapeTemplates rewrites the named templates, which must be
-// associated with t, to guarantee that the output of any of the named
-// templates is properly escaped. Names should include the names of
-// all templates that might be Executed but need not include helper
-// templates. If no error is returned, then the named templates have
-// been modified. Otherwise the named templates have been rendered
-// unusable.
-func escapeTemplates(tmpl *Template, names ...string) error {
- e := newEscaper(tmpl)
- for _, name := range names {
- c, _ := e.escapeTree(context{}, name, 0)
- var err error
- if c.err != nil {
- err, c.err.Name = c.err, name
- } else if c.state != stateText {
- err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)}
- }
- if err != nil {
- // Prevent execution of unsafe templates.
- for _, name := range names {
- if t := tmpl.set[name]; t != nil {
- t.text.Tree = nil
- t.Tree = nil
- }
- }
- return err
- }
- }
- e.commit()
- for _, name := range names {
- if t := tmpl.set[name]; t != nil {
- t.escaped = true
- t.Tree = t.text.Tree
- }
- }
- return nil
-}
-
-// funcMap maps command names to functions that render their inputs safe.
-var funcMap = template.FuncMap{
- "html_template_attrescaper": attrEscaper,
- "html_template_commentescaper": commentEscaper,
- "html_template_cssescaper": cssEscaper,
- "html_template_cssvaluefilter": cssValueFilter,
- "html_template_htmlnamefilter": htmlNameFilter,
- "html_template_htmlescaper": htmlEscaper,
- "html_template_jsregexpescaper": jsRegexpEscaper,
- "html_template_jsstrescaper": jsStrEscaper,
- "html_template_jsvalescaper": jsValEscaper,
- "html_template_nospaceescaper": htmlNospaceEscaper,
- "html_template_rcdataescaper": rcdataEscaper,
- "html_template_urlescaper": urlEscaper,
- "html_template_urlfilter": urlFilter,
- "html_template_urlnormalizer": urlNormalizer,
-}
-
-// equivEscapers matches contextual escapers to equivalent template builtins.
-var equivEscapers = map[string]string{
- "html_template_attrescaper": "html",
- "html_template_htmlescaper": "html",
- "html_template_nospaceescaper": "html",
- "html_template_rcdataescaper": "html",
- "html_template_urlescaper": "urlquery",
- "html_template_urlnormalizer": "urlquery",
-}
-
-// escaper collects type inferences about templates and changes needed to make
-// templates injection safe.
-type escaper struct {
- tmpl *Template
- // output[templateName] is the output context for a templateName that
- // has been mangled to include its input context.
- output map[string]context
- // derived[c.mangle(name)] maps to a template derived from the template
- // named name templateName for the start context c.
- derived map[string]*template.Template
- // called[templateName] is a set of called mangled template names.
- called map[string]bool
- // xxxNodeEdits are the accumulated edits to apply during commit.
- // Such edits are not applied immediately in case a template set
- // executes a given template in different escaping contexts.
- actionNodeEdits map[*parse.ActionNode][]string
- templateNodeEdits map[*parse.TemplateNode]string
- textNodeEdits map[*parse.TextNode][]byte
-}
-
-// newEscaper creates a blank escaper for the given set.
-func newEscaper(t *Template) *escaper {
- return &escaper{
- t,
- map[string]context{},
- map[string]*template.Template{},
- map[string]bool{},
- map[*parse.ActionNode][]string{},
- map[*parse.TemplateNode]string{},
- map[*parse.TextNode][]byte{},
- }
-}
-
-// filterFailsafe is an innocuous word that is emitted in place of unsafe values
-// by sanitizer functions. It is not a keyword in any programming language,
-// contains no special characters, is not empty, and when it appears in output
-// it is distinct enough that a developer can find the source of the problem
-// via a search engine.
-const filterFailsafe = "ZgotmplZ"
-
-// escape escapes a template node.
-func (e *escaper) escape(c context, n parse.Node) context {
- switch n := n.(type) {
- case *parse.ActionNode:
- return e.escapeAction(c, n)
- case *parse.IfNode:
- return e.escapeBranch(c, &n.BranchNode, "if")
- case *parse.ListNode:
- return e.escapeList(c, n)
- case *parse.RangeNode:
- return e.escapeBranch(c, &n.BranchNode, "range")
- case *parse.TemplateNode:
- return e.escapeTemplate(c, n)
- case *parse.TextNode:
- return e.escapeText(c, n)
- case *parse.WithNode:
- return e.escapeBranch(c, &n.BranchNode, "with")
- }
- panic("escaping " + n.String() + " is unimplemented")
-}
-
-// escapeAction escapes an action template node.
-func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
- if len(n.Pipe.Decl) != 0 {
- // A local variable assignment, not an interpolation.
- return c
- }
- c = nudge(c)
- s := make([]string, 0, 3)
- switch c.state {
- case stateError:
- return c
- case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL:
- switch c.urlPart {
- case urlPartNone:
- s = append(s, "html_template_urlfilter")
- fallthrough
- case urlPartPreQuery:
- switch c.state {
- case stateCSSDqStr, stateCSSSqStr:
- s = append(s, "html_template_cssescaper")
- default:
- s = append(s, "html_template_urlnormalizer")
- }
- case urlPartQueryOrFrag:
- s = append(s, "html_template_urlescaper")
- case urlPartUnknown:
- return context{
- state: stateError,
- err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n),
- }
- default:
- panic(c.urlPart.String())
- }
- case stateJS:
- s = append(s, "html_template_jsvalescaper")
- // A slash after a value starts a div operator.
- c.jsCtx = jsCtxDivOp
- case stateJSDqStr, stateJSSqStr:
- s = append(s, "html_template_jsstrescaper")
- case stateJSRegexp:
- s = append(s, "html_template_jsregexpescaper")
- case stateCSS:
- s = append(s, "html_template_cssvaluefilter")
- case stateText:
- s = append(s, "html_template_htmlescaper")
- case stateRCDATA:
- s = append(s, "html_template_rcdataescaper")
- case stateAttr:
- // Handled below in delim check.
- case stateAttrName, stateTag:
- c.state = stateAttrName
- s = append(s, "html_template_htmlnamefilter")
- default:
- if isComment(c.state) {
- s = append(s, "html_template_commentescaper")
- } else {
- panic("unexpected state " + c.state.String())
- }
- }
- switch c.delim {
- case delimNone:
- // No extra-escaping needed for raw text content.
- case delimSpaceOrTagEnd:
- s = append(s, "html_template_nospaceescaper")
- default:
- s = append(s, "html_template_attrescaper")
- }
- e.editActionNode(n, s)
- return c
-}
-
-// allIdents returns the names of the identifiers under the Ident field of the node,
-// which might be a singleton (Identifier) or a slice (Field).
-func allIdents(node parse.Node) []string {
- switch node := node.(type) {
- case *parse.IdentifierNode:
- return []string{node.Ident}
- case *parse.FieldNode:
- return node.Ident
- }
- panic("unidentified node type in allIdents")
-}
-
-// ensurePipelineContains ensures that the pipeline has commands with
-// the identifiers in s in order.
-// If the pipeline already has some of the sanitizers, do not interfere.
-// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it
-// has one matching, "html", and one to insert, "escapeJSVal", to produce
-// (.X | escapeJSVal | html).
-func ensurePipelineContains(p *parse.PipeNode, s []string) {
- if len(s) == 0 {
- return
- }
- n := len(p.Cmds)
- // Find the identifiers at the end of the command chain.
- idents := p.Cmds
- for i := n - 1; i >= 0; i-- {
- if cmd := p.Cmds[i]; len(cmd.Args) != 0 {
- if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
- continue
- }
- }
- idents = p.Cmds[i+1:]
- }
- dups := 0
- for _, idNode := range idents {
- for _, ident := range allIdents(idNode.Args[0]) {
- if escFnsEq(s[dups], ident) {
- dups++
- if dups == len(s) {
- return
- }
- }
- }
- }
- newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups)
- copy(newCmds, p.Cmds)
- // Merge existing identifier commands with the sanitizers needed.
- for _, idNode := range idents {
- pos := idNode.Args[0].Position()
- for _, ident := range allIdents(idNode.Args[0]) {
- i := indexOfStr(ident, s, escFnsEq)
- if i != -1 {
- for _, name := range s[:i] {
- newCmds = appendCmd(newCmds, newIdentCmd(name, pos))
- }
- s = s[i+1:]
- }
- }
- newCmds = appendCmd(newCmds, idNode)
- }
- // Create any remaining sanitizers.
- for _, name := range s {
- newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position()))
- }
- p.Cmds = newCmds
-}
-
-// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
-// for all x.
-var redundantFuncs = map[string]map[string]bool{
- "html_template_commentescaper": {
- "html_template_attrescaper": true,
- "html_template_nospaceescaper": true,
- "html_template_htmlescaper": true,
- },
- "html_template_cssescaper": {
- "html_template_attrescaper": true,
- },
- "html_template_jsregexpescaper": {
- "html_template_attrescaper": true,
- },
- "html_template_jsstrescaper": {
- "html_template_attrescaper": true,
- },
- "html_template_urlescaper": {
- "html_template_urlnormalizer": true,
- },
-}
-
-// appendCmd appends the given command to the end of the command pipeline
-// unless it is redundant with the last command.
-func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode {
- if n := len(cmds); n != 0 {
- last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode)
- next, _ := cmd.Args[0].(*parse.IdentifierNode)
- if ok && redundantFuncs[last.Ident][next.Ident] {
- return cmds
- }
- }
- return append(cmds, cmd)
-}
-
-// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found.
-func indexOfStr(s string, strs []string, eq func(a, b string) bool) int {
- for i, t := range strs {
- if eq(s, t) {
- return i
- }
- }
- return -1
-}
-
-// escFnsEq reports whether the two escaping functions are equivalent.
-func escFnsEq(a, b string) bool {
- if e := equivEscapers[a]; e != "" {
- a = e
- }
- if e := equivEscapers[b]; e != "" {
- b = e
- }
- return a == b
-}
-
-// newIdentCmd produces a command containing a single identifier node.
-func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
- return &parse.CommandNode{
- NodeType: parse.NodeCommand,
- Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)},
- }
-}
-
-// nudge returns the context that would result from following empty string
-// transitions from the input context.
-// For example, parsing:
-// `<a href=`
-// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune:
-// `<a href=x`
-// will end in context{stateURL, delimSpaceOrTagEnd, ...}.
-// There are two transitions that happen when the 'x' is seen:
-// (1) Transition from a before-value state to a start-of-value state without
-// consuming any character.
-// (2) Consume 'x' and transition past the first value character.
-// In this case, nudging produces the context after (1) happens.
-func nudge(c context) context {
- switch c.state {
- case stateTag:
- // In `<foo {{.}}`, the action should emit an attribute.
- c.state = stateAttrName
- case stateBeforeValue:
- // In `<foo bar={{.}}`, the action is an undelimited value.
- c.state, c.delim, c.attr = attrStartStates[c.attr], delimSpaceOrTagEnd, attrNone
- case stateAfterName:
- // In `<foo bar {{.}}`, the action is an attribute name.
- c.state, c.attr = stateAttrName, attrNone
- }
- return c
-}
-
-// join joins the two contexts of a branch template node. The result is an
-// error context if either of the input contexts are error contexts, or if the
-// the input contexts differ.
-func join(a, b context, line int, nodeName string) context {
- if a.state == stateError {
- return a
- }
- if b.state == stateError {
- return b
- }
- if a.eq(b) {
- return a
- }
-
- c := a
- c.urlPart = b.urlPart
- if c.eq(b) {
- // The contexts differ only by urlPart.
- c.urlPart = urlPartUnknown
- return c
- }
-
- c = a
- c.jsCtx = b.jsCtx
- if c.eq(b) {
- // The contexts differ only by jsCtx.
- c.jsCtx = jsCtxUnknown
- return c
- }
-
- // Allow a nudged context to join with an unnudged one.
- // This means that
- // <p title={{if .C}}{{.}}{{end}}
- // ends in an unquoted value state even though the else branch
- // ends in stateBeforeValue.
- if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) {
- if e := join(c, d, line, nodeName); e.state != stateError {
- return e
- }
- }
-
- return context{
- state: stateError,
- err: errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
- }
-}
-
-// escapeBranch escapes a branch template node: "if", "range" and "with".
-func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
- c0 := e.escapeList(c, n.List)
- if nodeName == "range" && c0.state != stateError {
- // The "true" branch of a "range" node can execute multiple times.
- // We check that executing n.List once results in the same context
- // as executing n.List twice.
- c1, _ := e.escapeListConditionally(c0, n.List, nil)
- c0 = join(c0, c1, n.Line, nodeName)
- if c0.state == stateError {
- // Make clear that this is a problem on loop re-entry
- // since developers tend to overlook that branch when
- // debugging templates.
- c0.err.Line = n.Line
- c0.err.Description = "on range loop re-entry: " + c0.err.Description
- return c0
- }
- }
- c1 := e.escapeList(c, n.ElseList)
- return join(c0, c1, n.Line, nodeName)
-}
-
-// escapeList escapes a list template node.
-func (e *escaper) escapeList(c context, n *parse.ListNode) context {
- if n == nil {
- return c
- }
- for _, m := range n.Nodes {
- c = e.escape(c, m)
- }
- return c
-}
-
-// escapeListConditionally escapes a list node but only preserves edits and
-// inferences in e if the inferences and output context satisfy filter.
-// It returns the best guess at an output context, and the result of the filter
-// which is the same as whether e was updated.
-func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
- e1 := newEscaper(e.tmpl)
- // Make type inferences available to f.
- for k, v := range e.output {
- e1.output[k] = v
- }
- c = e1.escapeList(c, n)
- ok := filter != nil && filter(e1, c)
- if ok {
- // Copy inferences and edits from e1 back into e.
- for k, v := range e1.output {
- e.output[k] = v
- }
- for k, v := range e1.derived {
- e.derived[k] = v
- }
- for k, v := range e1.called {
- e.called[k] = v
- }
- for k, v := range e1.actionNodeEdits {
- e.editActionNode(k, v)
- }
- for k, v := range e1.templateNodeEdits {
- e.editTemplateNode(k, v)
- }
- for k, v := range e1.textNodeEdits {
- e.editTextNode(k, v)
- }
- }
- return c, ok
-}
-
-// escapeTemplate escapes a {{template}} call node.
-func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context {
- c, name := e.escapeTree(c, n.Name, n.Line)
- if name != n.Name {
- e.editTemplateNode(n, name)
- }
- return c
-}
-
-// escapeTree escapes the named template starting in the given context as
-// necessary and returns its output context.
-func (e *escaper) escapeTree(c context, name string, line int) (context, string) {
- // Mangle the template name with the input context to produce a reliable
- // identifier.
- dname := c.mangle(name)
- e.called[dname] = true
- if out, ok := e.output[dname]; ok {
- // Already escaped.
- return out, dname
- }
- t := e.template(name)
- if t == nil {
- // Two cases: The template exists but is empty, or has never been mentioned at
- // all. Distinguish the cases in the error messages.
- if e.tmpl.set[name] != nil {
- return context{
- state: stateError,
- err: errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name),
- }, dname
- }
- return context{
- state: stateError,
- err: errorf(ErrNoSuchTemplate, line, "no such template %q", name),
- }, dname
- }
- if dname != name {
- // Use any template derived during an earlier call to escapeTemplate
- // with different top level templates, or clone if necessary.
- dt := e.template(dname)
- if dt == nil {
- dt = template.New(dname)
- dt.Tree = &parse.Tree{Name: dname, Root: t.Root.CopyList()}
- e.derived[dname] = dt
- }
- t = dt
- }
- return e.computeOutCtx(c, t), dname
-}
-
-// computeOutCtx takes a template and its start context and computes the output
-// context while storing any inferences in e.
-func (e *escaper) computeOutCtx(c context, t *template.Template) context {
- // Propagate context over the body.
- c1, ok := e.escapeTemplateBody(c, t)
- if !ok {
- // Look for a fixed point by assuming c1 as the output context.
- if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 {
- c1, ok = c2, true
- }
- // Use c1 as the error context if neither assumption worked.
- }
- if !ok && c1.state != stateError {
- return context{
- state: stateError,
- // TODO: Find the first node with a line in t.text.Tree.Root
- err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()),
- }
- }
- return c1
-}
-
-// escapeTemplateBody escapes the given template assuming the given output
-// context, and returns the best guess at the output context and whether the
-// assumption was correct.
-func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) {
- filter := func(e1 *escaper, c1 context) bool {
- if c1.state == stateError {
- // Do not update the input escaper, e.
- return false
- }
- if !e1.called[t.Name()] {
- // If t is not recursively called, then c1 is an
- // accurate output context.
- return true
- }
- // c1 is accurate if it matches our assumed output context.
- return c.eq(c1)
- }
- // We need to assume an output context so that recursive template calls
- // take the fast path out of escapeTree instead of infinitely recursing.
- // Naively assuming that the input context is the same as the output
- // works >90% of the time.
- e.output[t.Name()] = c
- return e.escapeListConditionally(c, t.Tree.Root, filter)
-}
-
-// delimEnds maps each delim to a string of characters that terminate it.
-var delimEnds = [...]string{
- delimDoubleQuote: `"`,
- delimSingleQuote: "'",
- // Determined empirically by running the below in various browsers.
- // var div = document.createElement("DIV");
- // for (var i = 0; i < 0x10000; ++i) {
- // div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>";
- // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0)
- // document.write("<p>U+" + i.toString(16));
- // }
- delimSpaceOrTagEnd: " \t\n\f\r>",
-}
-
-var doctypeBytes = []byte("<!DOCTYPE")
-
-// escapeText escapes a text template node.
-func (e *escaper) escapeText(c context, n *parse.TextNode) context {
- s, written, i, b := n.Text, 0, 0, new(bytes.Buffer)
- for i != len(s) {
- c1, nread := contextAfterText(c, s[i:])
- i1 := i + nread
- if c.state == stateText || c.state == stateRCDATA {
- end := i1
- if c1.state != c.state {
- for j := end - 1; j >= i; j-- {
- if s[j] == '<' {
- end = j
- break
- }
- }
- }
- for j := i; j < end; j++ {
- if s[j] == '<' && !bytes.HasPrefix(bytes.ToUpper(s[j:]), doctypeBytes) {
- b.Write(s[written:j])
- b.WriteString("&lt;")
- written = j + 1
- }
- }
- } else if isComment(c.state) && c.delim == delimNone {
- switch c.state {
- case stateJSBlockCmt:
- // http://es5.github.com/#x7.4:
- // "Comments behave like white space and are
- // discarded except that, if a MultiLineComment
- // contains a line terminator character, then
- // the entire comment is considered to be a
- // LineTerminator for purposes of parsing by
- // the syntactic grammar."
- if bytes.IndexAny(s[written:i1], "\n\r\u2028\u2029") != -1 {
- b.WriteByte('\n')
- } else {
- b.WriteByte(' ')
- }
- case stateCSSBlockCmt:
- b.WriteByte(' ')
- }
- written = i1
- }
- if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone {
- // Preserve the portion between written and the comment start.
- cs := i1 - 2
- if c1.state == stateHTMLCmt {
- // "<!--" instead of "/*" or "//"
- cs -= 2
- }
- b.Write(s[written:cs])
- written = i1
- }
- if i == i1 && c.state == c1.state {
- panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
- }
- c, i = c1, i1
- }
-
- if written != 0 && c.state != stateError {
- if !isComment(c.state) || c.delim != delimNone {
- b.Write(n.Text[written:])
- }
- e.editTextNode(n, b.Bytes())
- }
- return c
-}
-
-// contextAfterText starts in context c, consumes some tokens from the front of
-// s, then returns the context after those tokens and the unprocessed suffix.
-func contextAfterText(c context, s []byte) (context, int) {
- if c.delim == delimNone {
- c1, i := tSpecialTagEnd(c, s)
- if i == 0 {
- // A special end tag (`</script>`) has been seen and
- // all content preceding it has been consumed.
- return c1, 0
- }
- // Consider all content up to any end tag.
- return transitionFunc[c.state](c, s[:i])
- }
-
- i := bytes.IndexAny(s, delimEnds[c.delim])
- if i == -1 {
- i = len(s)
- }
- if c.delim == delimSpaceOrTagEnd {
- // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
- // lists the runes below as error characters.
- // Error out because HTML parsers may differ on whether
- // "<a id= onclick=f(" ends inside id's or onclick's value,
- // "<a class=`foo " ends inside a value,
- // "<a style=font:'Arial'" needs open-quote fixup.
- // IE treats '`' as a quotation character.
- if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 {
- return context{
- state: stateError,
- err: errorf(ErrBadHTML, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]),
- }, len(s)
- }
- }
- if i == len(s) {
- // Remain inside the attribute.
- // Decode the value so non-HTML rules can easily handle
- // <button onclick="alert(&quot;Hi!&quot;)">
- // without having to entity decode token boundaries.
- for u := []byte(html.UnescapeString(string(s))); len(u) != 0; {
- c1, i1 := transitionFunc[c.state](c, u)
- c, u = c1, u[i1:]
- }
- return c, len(s)
- }
- if c.delim != delimSpaceOrTagEnd {
- // Consume any quote.
- i++
- }
- // On exiting an attribute, we discard all state information
- // except the state and element.
- return context{state: stateTag, element: c.element}, i
-}
-
-// editActionNode records a change to an action pipeline for later commit.
-func (e *escaper) editActionNode(n *parse.ActionNode, cmds []string) {
- if _, ok := e.actionNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.actionNodeEdits[n] = cmds
-}
-
-// editTemplateNode records a change to a {{template}} callee for later commit.
-func (e *escaper) editTemplateNode(n *parse.TemplateNode, callee string) {
- if _, ok := e.templateNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.templateNodeEdits[n] = callee
-}
-
-// editTextNode records a change to a text node for later commit.
-func (e *escaper) editTextNode(n *parse.TextNode, text []byte) {
- if _, ok := e.textNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.textNodeEdits[n] = text
-}
-
-// commit applies changes to actions and template calls needed to contextually
-// autoescape content and adds any derived templates to the set.
-func (e *escaper) commit() {
- for name := range e.output {
- e.template(name).Funcs(funcMap)
- }
- for _, t := range e.derived {
- if _, err := e.tmpl.text.AddParseTree(t.Name(), t.Tree); err != nil {
- panic("error adding derived template")
- }
- }
- for n, s := range e.actionNodeEdits {
- ensurePipelineContains(n.Pipe, s)
- }
- for n, name := range e.templateNodeEdits {
- n.Name = name
- }
- for n, s := range e.textNodeEdits {
- n.Text = s
- }
-}
-
-// template returns the named template given a mangled template name.
-func (e *escaper) template(name string) *template.Template {
- t := e.tmpl.text.Lookup(name)
- if t == nil {
- t = e.derived[name]
- }
- return t
-}
-
-// Forwarding functions so that clients need only import this package
-// to reach the general escaping functions of text/template.
-
-// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
-func HTMLEscape(w io.Writer, b []byte) {
- template.HTMLEscape(w, b)
-}
-
-// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
-func HTMLEscapeString(s string) string {
- return template.HTMLEscapeString(s)
-}
-
-// HTMLEscaper returns the escaped HTML equivalent of the textual
-// representation of its arguments.
-func HTMLEscaper(args ...interface{}) string {
- return template.HTMLEscaper(args...)
-}
-
-// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
-func JSEscape(w io.Writer, b []byte) {
- template.JSEscape(w, b)
-}
-
-// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
-func JSEscapeString(s string) string {
- return template.JSEscapeString(s)
-}
-
-// JSEscaper returns the escaped JavaScript equivalent of the textual
-// representation of its arguments.
-func JSEscaper(args ...interface{}) string {
- return template.JSEscaper(args...)
-}
-
-// URLQueryEscaper returns the escaped value of the textual representation of
-// its arguments in a form suitable for embedding in a URL query.
-func URLQueryEscaper(args ...interface{}) string {
- return template.URLQueryEscaper(args...)
-}
diff --git a/src/pkg/html/template/escape_test.go b/src/pkg/html/template/escape_test.go
deleted file mode 100644
index 3ccf93ece..000000000
--- a/src/pkg/html/template/escape_test.go
+++ /dev/null
@@ -1,1692 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "os"
- "strings"
- "testing"
- "text/template"
- "text/template/parse"
-)
-
-type badMarshaler struct{}
-
-func (x *badMarshaler) MarshalJSON() ([]byte, error) {
- // Keys in valid JSON must be double quoted as must all strings.
- return []byte("{ foo: 'not quite valid JSON' }"), nil
-}
-
-type goodMarshaler struct{}
-
-func (x *goodMarshaler) MarshalJSON() ([]byte, error) {
- return []byte(`{ "<foo>": "O'Reilly" }`), nil
-}
-
-func TestEscape(t *testing.T) {
- data := struct {
- F, T bool
- C, G, H string
- A, E []string
- B, M json.Marshaler
- N int
- Z *int
- W HTML
- }{
- F: false,
- T: true,
- C: "<Cincinatti>",
- G: "<Goodbye>",
- H: "<Hello>",
- A: []string{"<a>", "<b>"},
- E: []string{},
- N: 42,
- B: &badMarshaler{},
- M: &goodMarshaler{},
- Z: nil,
- W: HTML(`&iexcl;<b class="foo">Hello</b>, <textarea>O'World</textarea>!`),
- }
- pdata := &data
-
- tests := []struct {
- name string
- input string
- output string
- }{
- {
- "if",
- "{{if .T}}Hello{{end}}, {{.C}}!",
- "Hello, &lt;Cincinatti&gt;!",
- },
- {
- "else",
- "{{if .F}}{{.H}}{{else}}{{.G}}{{end}}!",
- "&lt;Goodbye&gt;!",
- },
- {
- "overescaping1",
- "Hello, {{.C | html}}!",
- "Hello, &lt;Cincinatti&gt;!",
- },
- {
- "overescaping2",
- "Hello, {{html .C}}!",
- "Hello, &lt;Cincinatti&gt;!",
- },
- {
- "overescaping3",
- "{{with .C}}{{$msg := .}}Hello, {{$msg}}!{{end}}",
- "Hello, &lt;Cincinatti&gt;!",
- },
- {
- "assignment",
- "{{if $x := .H}}{{$x}}{{end}}",
- "&lt;Hello&gt;",
- },
- {
- "withBody",
- "{{with .H}}{{.}}{{end}}",
- "&lt;Hello&gt;",
- },
- {
- "withElse",
- "{{with .E}}{{.}}{{else}}{{.H}}{{end}}",
- "&lt;Hello&gt;",
- },
- {
- "rangeBody",
- "{{range .A}}{{.}}{{end}}",
- "&lt;a&gt;&lt;b&gt;",
- },
- {
- "rangeElse",
- "{{range .E}}{{.}}{{else}}{{.H}}{{end}}",
- "&lt;Hello&gt;",
- },
- {
- "nonStringValue",
- "{{.T}}",
- "true",
- },
- {
- "constant",
- `<a href="/search?q={{"'a<b'"}}">`,
- `<a href="/search?q=%27a%3cb%27">`,
- },
- {
- "multipleAttrs",
- "<a b=1 c={{.H}}>",
- "<a b=1 c=&lt;Hello&gt;>",
- },
- {
- "urlStartRel",
- `<a href='{{"/foo/bar?a=b&c=d"}}'>`,
- `<a href='/foo/bar?a=b&amp;c=d'>`,
- },
- {
- "urlStartAbsOk",
- `<a href='{{"http://example.com/foo/bar?a=b&c=d"}}'>`,
- `<a href='http://example.com/foo/bar?a=b&amp;c=d'>`,
- },
- {
- "protocolRelativeURLStart",
- `<a href='{{"//example.com:8000/foo/bar?a=b&c=d"}}'>`,
- `<a href='//example.com:8000/foo/bar?a=b&amp;c=d'>`,
- },
- {
- "pathRelativeURLStart",
- `<a href="{{"/javascript:80/foo/bar"}}">`,
- `<a href="/javascript:80/foo/bar">`,
- },
- {
- "dangerousURLStart",
- `<a href='{{"javascript:alert(%22pwned%22)"}}'>`,
- `<a href='#ZgotmplZ'>`,
- },
- {
- "dangerousURLStart2",
- `<a href=' {{"javascript:alert(%22pwned%22)"}}'>`,
- `<a href=' #ZgotmplZ'>`,
- },
- {
- "nonHierURL",
- `<a href={{"mailto:Muhammed \"The Greatest\" Ali <m.ali@example.com>"}}>`,
- `<a href=mailto:Muhammed%20%22The%20Greatest%22%20Ali%20%3cm.ali@example.com%3e>`,
- },
- {
- "urlPath",
- `<a href='http://{{"javascript:80"}}/foo'>`,
- `<a href='http://javascript:80/foo'>`,
- },
- {
- "urlQuery",
- `<a href='/search?q={{.H}}'>`,
- `<a href='/search?q=%3cHello%3e'>`,
- },
- {
- "urlFragment",
- `<a href='/faq#{{.H}}'>`,
- `<a href='/faq#%3cHello%3e'>`,
- },
- {
- "urlBranch",
- `<a href="{{if .F}}/foo?a=b{{else}}/bar{{end}}">`,
- `<a href="/bar">`,
- },
- {
- "urlBranchConflictMoot",
- `<a href="{{if .T}}/foo?a={{else}}/bar#{{end}}{{.C}}">`,
- `<a href="/foo?a=%3cCincinatti%3e">`,
- },
- {
- "jsStrValue",
- "<button onclick='alert({{.H}})'>",
- `<button onclick='alert(&#34;\u003cHello\u003e&#34;)'>`,
- },
- {
- "jsNumericValue",
- "<button onclick='alert({{.N}})'>",
- `<button onclick='alert( 42 )'>`,
- },
- {
- "jsBoolValue",
- "<button onclick='alert({{.T}})'>",
- `<button onclick='alert( true )'>`,
- },
- {
- "jsNilValue",
- "<button onclick='alert(typeof{{.Z}})'>",
- `<button onclick='alert(typeof null )'>`,
- },
- {
- "jsObjValue",
- "<button onclick='alert({{.A}})'>",
- `<button onclick='alert([&#34;\u003ca\u003e&#34;,&#34;\u003cb\u003e&#34;])'>`,
- },
- {
- "jsObjValueScript",
- "<script>alert({{.A}})</script>",
- `<script>alert(["\u003ca\u003e","\u003cb\u003e"])</script>`,
- },
- {
- "jsObjValueNotOverEscaped",
- "<button onclick='alert({{.A | html}})'>",
- `<button onclick='alert([&#34;\u003ca\u003e&#34;,&#34;\u003cb\u003e&#34;])'>`,
- },
- {
- "jsStr",
- "<button onclick='alert(&quot;{{.H}}&quot;)'>",
- `<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
- },
- {
- "badMarshaler",
- `<button onclick='alert(1/{{.B}}in numbers)'>`,
- `<button onclick='alert(1/ /* json: error calling MarshalJSON for type *template.badMarshaler: invalid character &#39;f&#39; looking for beginning of object key string */null in numbers)'>`,
- },
- {
- "jsMarshaler",
- `<button onclick='alert({{.M}})'>`,
- `<button onclick='alert({&#34;\u003cfoo\u003e&#34;:&#34;O&#39;Reilly&#34;})'>`,
- },
- {
- "jsStrNotUnderEscaped",
- "<button onclick='alert({{.C | urlquery}})'>",
- // URL escaped, then quoted for JS.
- `<button onclick='alert(&#34;%3CCincinatti%3E&#34;)'>`,
- },
- {
- "jsRe",
- `<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`,
- `<button onclick='alert(/foo\x2bbar/.test(""))'>`,
- },
- {
- "jsReBlank",
- `<script>alert(/{{""}}/.test(""));</script>`,
- `<script>alert(/(?:)/.test(""));</script>`,
- },
- {
- "jsReAmbigOk",
- `<script>{{if true}}var x = 1{{end}}</script>`,
- // The {if} ends in an ambiguous jsCtx but there is
- // no slash following so we shouldn't care.
- `<script>var x = 1</script>`,
- },
- {
- "styleBidiKeywordPassed",
- `<p style="dir: {{"ltr"}}">`,
- `<p style="dir: ltr">`,
- },
- {
- "styleBidiPropNamePassed",
- `<p style="border-{{"left"}}: 0; border-{{"right"}}: 1in">`,
- `<p style="border-left: 0; border-right: 1in">`,
- },
- {
- "styleExpressionBlocked",
- `<p style="width: {{"expression(alert(1337))"}}">`,
- `<p style="width: ZgotmplZ">`,
- },
- {
- "styleTagSelectorPassed",
- `<style>{{"p"}} { color: pink }</style>`,
- `<style>p { color: pink }</style>`,
- },
- {
- "styleIDPassed",
- `<style>p{{"#my-ID"}} { font: Arial }</style>`,
- `<style>p#my-ID { font: Arial }</style>`,
- },
- {
- "styleClassPassed",
- `<style>p{{".my_class"}} { font: Arial }</style>`,
- `<style>p.my_class { font: Arial }</style>`,
- },
- {
- "styleQuantityPassed",
- `<a style="left: {{"2em"}}; top: {{0}}">`,
- `<a style="left: 2em; top: 0">`,
- },
- {
- "stylePctPassed",
- `<table style=width:{{"100%"}}>`,
- `<table style=width:100%>`,
- },
- {
- "styleColorPassed",
- `<p style="color: {{"#8ff"}}; background: {{"#000"}}">`,
- `<p style="color: #8ff; background: #000">`,
- },
- {
- "styleObfuscatedExpressionBlocked",
- `<p style="width: {{" e\\78preS\x00Sio/**/n(alert(1337))"}}">`,
- `<p style="width: ZgotmplZ">`,
- },
- {
- "styleMozBindingBlocked",
- `<p style="{{"-moz-binding(alert(1337))"}}: ...">`,
- `<p style="ZgotmplZ: ...">`,
- },
- {
- "styleObfuscatedMozBindingBlocked",
- `<p style="{{" -mo\\7a-B\x00I/**/nding(alert(1337))"}}: ...">`,
- `<p style="ZgotmplZ: ...">`,
- },
- {
- "styleFontNameString",
- `<p style='font-family: "{{"Times New Roman"}}"'>`,
- `<p style='font-family: "Times New Roman"'>`,
- },
- {
- "styleFontNameString",
- `<p style='font-family: "{{"Times New Roman"}}", "{{"sans-serif"}}"'>`,
- `<p style='font-family: "Times New Roman", "sans-serif"'>`,
- },
- {
- "styleFontNameUnquoted",
- `<p style='font-family: {{"Times New Roman"}}'>`,
- `<p style='font-family: Times New Roman'>`,
- },
- {
- "styleURLQueryEncoded",
- `<p style="background: url(/img?name={{"O'Reilly Animal(1)<2>.png"}})">`,
- `<p style="background: url(/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png)">`,
- },
- {
- "styleQuotedURLQueryEncoded",
- `<p style="background: url('/img?name={{"O'Reilly Animal(1)<2>.png"}}')">`,
- `<p style="background: url('/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png')">`,
- },
- {
- "styleStrQueryEncoded",
- `<p style="background: '/img?name={{"O'Reilly Animal(1)<2>.png"}}'">`,
- `<p style="background: '/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png'">`,
- },
- {
- "styleURLBadProtocolBlocked",
- `<a style="background: url('{{"javascript:alert(1337)"}}')">`,
- `<a style="background: url('#ZgotmplZ')">`,
- },
- {
- "styleStrBadProtocolBlocked",
- `<a style="background: '{{"vbscript:alert(1337)"}}'">`,
- `<a style="background: '#ZgotmplZ'">`,
- },
- {
- "styleStrEncodedProtocolEncoded",
- `<a style="background: '{{"javascript\\3a alert(1337)"}}'">`,
- // The CSS string 'javascript\\3a alert(1337)' does not contains a colon.
- `<a style="background: 'javascript\\3a alert\28 1337\29 '">`,
- },
- {
- "styleURLGoodProtocolPassed",
- `<a style="background: url('{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}')">`,
- `<a style="background: url('http://oreilly.com/O%27Reilly%20Animals%281%29%3c2%3e;%7b%7d.html')">`,
- },
- {
- "styleStrGoodProtocolPassed",
- `<a style="background: '{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}'">`,
- `<a style="background: 'http\3a\2f\2foreilly.com\2fO\27Reilly Animals\28 1\29\3c 2\3e\3b\7b\7d.html'">`,
- },
- {
- "styleURLEncodedForHTMLInAttr",
- `<a style="background: url('{{"/search?img=foo&size=icon"}}')">`,
- `<a style="background: url('/search?img=foo&amp;size=icon')">`,
- },
- {
- "styleURLNotEncodedForHTMLInCdata",
- `<style>body { background: url('{{"/search?img=foo&size=icon"}}') }</style>`,
- `<style>body { background: url('/search?img=foo&size=icon') }</style>`,
- },
- {
- "styleURLMixedCase",
- `<p style="background: URL(#{{.H}})">`,
- `<p style="background: URL(#%3cHello%3e)">`,
- },
- {
- "stylePropertyPairPassed",
- `<a style='{{"color: red"}}'>`,
- `<a style='color: red'>`,
- },
- {
- "styleStrSpecialsEncoded",
- `<a style="font-family: '{{"/**/'\";:// \\"}}', &quot;{{"/**/'\";:// \\"}}&quot;">`,
- `<a style="font-family: '\2f**\2f\27\22\3b\3a\2f\2f \\', &quot;\2f**\2f\27\22\3b\3a\2f\2f \\&quot;">`,
- },
- {
- "styleURLSpecialsEncoded",
- `<a style="border-image: url({{"/**/'\";:// \\"}}), url(&quot;{{"/**/'\";:// \\"}}&quot;), url('{{"/**/'\";:// \\"}}'), 'http://www.example.com/?q={{"/**/'\";:// \\"}}''">`,
- `<a style="border-image: url(/**/%27%22;://%20%5c), url(&quot;/**/%27%22;://%20%5c&quot;), url('/**/%27%22;://%20%5c'), 'http://www.example.com/?q=%2f%2a%2a%2f%27%22%3b%3a%2f%2f%20%5c''">`,
- },
- {
- "HTML comment",
- "<b>Hello, <!-- name of world -->{{.C}}</b>",
- "<b>Hello, &lt;Cincinatti&gt;</b>",
- },
- {
- "HTML comment not first < in text node.",
- "<<!-- -->!--",
- "&lt;!--",
- },
- {
- "HTML normalization 1",
- "a < b",
- "a &lt; b",
- },
- {
- "HTML normalization 2",
- "a << b",
- "a &lt;&lt; b",
- },
- {
- "HTML normalization 3",
- "a<<!-- --><!-- -->b",
- "a&lt;b",
- },
- {
- "HTML doctype not normalized",
- "<!DOCTYPE html>Hello, World!",
- "<!DOCTYPE html>Hello, World!",
- },
- {
- "HTML doctype not case-insensitive",
- "<!doCtYPE htMl>Hello, World!",
- "<!doCtYPE htMl>Hello, World!",
- },
- {
- "No doctype injection",
- `<!{{"DOCTYPE"}}`,
- "&lt;!DOCTYPE",
- },
- {
- "Split HTML comment",
- "<b>Hello, <!-- name of {{if .T}}city -->{{.C}}{{else}}world -->{{.W}}{{end}}</b>",
- "<b>Hello, &lt;Cincinatti&gt;</b>",
- },
- {
- "JS line comment",
- "<script>for (;;) { if (c()) break// foo not a label\n" +
- "foo({{.T}});}</script>",
- "<script>for (;;) { if (c()) break\n" +
- "foo( true );}</script>",
- },
- {
- "JS multiline block comment",
- "<script>for (;;) { if (c()) break/* foo not a label\n" +
- " */foo({{.T}});}</script>",
- // Newline separates break from call. If newline
- // removed, then break will consume label leaving
- // code invalid.
- "<script>for (;;) { if (c()) break\n" +
- "foo( true );}</script>",
- },
- {
- "JS single-line block comment",
- "<script>for (;;) {\n" +
- "if (c()) break/* foo a label */foo;" +
- "x({{.T}});}</script>",
- // Newline separates break from call. If newline
- // removed, then break will consume label leaving
- // code invalid.
- "<script>for (;;) {\n" +
- "if (c()) break foo;" +
- "x( true );}</script>",
- },
- {
- "JS block comment flush with mathematical division",
- "<script>var a/*b*//c\nd</script>",
- "<script>var a /c\nd</script>",
- },
- {
- "JS mixed comments",
- "<script>var a/*b*///c\nd</script>",
- "<script>var a \nd</script>",
- },
- {
- "CSS comments",
- "<style>p// paragraph\n" +
- `{border: 1px/* color */{{"#00f"}}}</style>`,
- "<style>p\n" +
- "{border: 1px #00f}</style>",
- },
- {
- "JS attr block comment",
- `<a onclick="f(&quot;&quot;); /* alert({{.H}}) */">`,
- // Attribute comment tests should pass if the comments
- // are successfully elided.
- `<a onclick="f(&quot;&quot;); /* alert() */">`,
- },
- {
- "JS attr line comment",
- `<a onclick="// alert({{.G}})">`,
- `<a onclick="// alert()">`,
- },
- {
- "CSS attr block comment",
- `<a style="/* color: {{.H}} */">`,
- `<a style="/* color: */">`,
- },
- {
- "CSS attr line comment",
- `<a style="// color: {{.G}}">`,
- `<a style="// color: ">`,
- },
- {
- "HTML substitution commented out",
- "<p><!-- {{.H}} --></p>",
- "<p></p>",
- },
- {
- "Comment ends flush with start",
- "<!--{{.}}--><script>/*{{.}}*///{{.}}\n</script><style>/*{{.}}*///{{.}}\n</style><a onclick='/*{{.}}*///{{.}}' style='/*{{.}}*///{{.}}'>",
- "<script> \n</script><style> \n</style><a onclick='/**///' style='/**///'>",
- },
- {
- "typed HTML in text",
- `{{.W}}`,
- `&iexcl;<b class="foo">Hello</b>, <textarea>O'World</textarea>!`,
- },
- {
- "typed HTML in attribute",
- `<div title="{{.W}}">`,
- `<div title="&iexcl;Hello, O&#39;World!">`,
- },
- {
- "typed HTML in script",
- `<button onclick="alert({{.W}})">`,
- `<button onclick="alert(&#34;\u0026iexcl;\u003cb class=\&#34;foo\&#34;\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO&#39;World\u003c/textarea\u003e!&#34;)">`,
- },
- {
- "typed HTML in RCDATA",
- `<textarea>{{.W}}</textarea>`,
- `<textarea>&iexcl;&lt;b class=&#34;foo&#34;&gt;Hello&lt;/b&gt;, &lt;textarea&gt;O&#39;World&lt;/textarea&gt;!</textarea>`,
- },
- {
- "range in textarea",
- "<textarea>{{range .A}}{{.}}{{end}}</textarea>",
- "<textarea>&lt;a&gt;&lt;b&gt;</textarea>",
- },
- {
- "No tag injection",
- `{{"10$"}}<{{"script src,evil.org/pwnd.js"}}...`,
- `10$&lt;script src,evil.org/pwnd.js...`,
- },
- {
- "No comment injection",
- `<{{"!--"}}`,
- `&lt;!--`,
- },
- {
- "No RCDATA end tag injection",
- `<textarea><{{"/textarea "}}...</textarea>`,
- `<textarea>&lt;/textarea ...</textarea>`,
- },
- {
- "optional attrs",
- `<img class="{{"iconClass"}}"` +
- `{{if .T}} id="{{"<iconId>"}}"{{end}}` +
- // Double quotes inside if/else.
- ` src=` +
- `{{if .T}}"?{{"<iconPath>"}}"` +
- `{{else}}"images/cleardot.gif"{{end}}` +
- // Missing space before title, but it is not a
- // part of the src attribute.
- `{{if .T}}title="{{"<title>"}}"{{end}}` +
- // Quotes outside if/else.
- ` alt="` +
- `{{if .T}}{{"<alt>"}}` +
- `{{else}}{{if .F}}{{"<title>"}}{{end}}` +
- `{{end}}"` +
- `>`,
- `<img class="iconClass" id="&lt;iconId&gt;" src="?%3ciconPath%3e"title="&lt;title&gt;" alt="&lt;alt&gt;">`,
- },
- {
- "conditional valueless attr name",
- `<input{{if .T}} checked{{end}} name=n>`,
- `<input checked name=n>`,
- },
- {
- "conditional dynamic valueless attr name 1",
- `<input{{if .T}} {{"checked"}}{{end}} name=n>`,
- `<input checked name=n>`,
- },
- {
- "conditional dynamic valueless attr name 2",
- `<input {{if .T}}{{"checked"}} {{end}}name=n>`,
- `<input checked name=n>`,
- },
- {
- "dynamic attribute name",
- `<img on{{"load"}}="alert({{"loaded"}})">`,
- // Treated as JS since quotes are inserted.
- `<img onload="alert(&#34;loaded&#34;)">`,
- },
- {
- "bad dynamic attribute name 1",
- // Allow checked, selected, disabled, but not JS or
- // CSS attributes.
- `<input {{"onchange"}}="{{"doEvil()"}}">`,
- `<input ZgotmplZ="doEvil()">`,
- },
- {
- "bad dynamic attribute name 2",
- `<div {{"sTyle"}}="{{"color: expression(alert(1337))"}}">`,
- `<div ZgotmplZ="color: expression(alert(1337))">`,
- },
- {
- "bad dynamic attribute name 3",
- // Allow title or alt, but not a URL.
- `<img {{"src"}}="{{"javascript:doEvil()"}}">`,
- `<img ZgotmplZ="javascript:doEvil()">`,
- },
- {
- "bad dynamic attribute name 4",
- // Structure preservation requires values to associate
- // with a consistent attribute.
- `<input checked {{""}}="Whose value am I?">`,
- `<input checked ZgotmplZ="Whose value am I?">`,
- },
- {
- "dynamic element name",
- `<h{{3}}><table><t{{"head"}}>...</h{{3}}>`,
- `<h3><table><thead>...</h3>`,
- },
- {
- "bad dynamic element name",
- // Dynamic element names are typically used to switch
- // between (thead, tfoot, tbody), (ul, ol), (th, td),
- // and other replaceable sets.
- // We do not currently easily support (ul, ol).
- // If we do change to support that, this test should
- // catch failures to filter out special tag names which
- // would violate the structure preservation property --
- // if any special tag name could be substituted, then
- // the content could be raw text/RCDATA for some inputs
- // and regular HTML content for others.
- `<{{"script"}}>{{"doEvil()"}}</{{"script"}}>`,
- `&lt;script>doEvil()&lt;/script>`,
- },
- }
-
- for _, test := range tests {
- tmpl := New(test.name)
- tmpl = Must(tmpl.Parse(test.input))
- // Check for bug 6459: Tree field was not set in Parse.
- if tmpl.Tree != tmpl.text.Tree {
- t.Errorf("%s: tree not set properly", test.name)
- continue
- }
- b := new(bytes.Buffer)
- if err := tmpl.Execute(b, data); err != nil {
- t.Errorf("%s: template execution failed: %s", test.name, err)
- continue
- }
- if w, g := test.output, b.String(); w != g {
- t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
- continue
- }
- b.Reset()
- if err := tmpl.Execute(b, pdata); err != nil {
- t.Errorf("%s: template execution failed for pointer: %s", test.name, err)
- continue
- }
- if w, g := test.output, b.String(); w != g {
- t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
- continue
- }
- if tmpl.Tree != tmpl.text.Tree {
- t.Errorf("%s: tree mismatch", test.name)
- continue
- }
- }
-}
-
-func TestEscapeSet(t *testing.T) {
- type dataItem struct {
- Children []*dataItem
- X string
- }
-
- data := dataItem{
- Children: []*dataItem{
- {X: "foo"},
- {X: "<bar>"},
- {
- Children: []*dataItem{
- {X: "baz"},
- },
- },
- },
- }
-
- tests := []struct {
- inputs map[string]string
- want string
- }{
- // The trivial set.
- {
- map[string]string{
- "main": ``,
- },
- ``,
- },
- // A template called in the start context.
- {
- map[string]string{
- "main": `Hello, {{template "helper"}}!`,
- // Not a valid top level HTML template.
- // "<b" is not a full tag.
- "helper": `{{"<World>"}}`,
- },
- `Hello, &lt;World&gt;!`,
- },
- // A template called in a context other than the start.
- {
- map[string]string{
- "main": `<a onclick='a = {{template "helper"}};'>`,
- // Not a valid top level HTML template.
- // "<b" is not a full tag.
- "helper": `{{"<a>"}}<b`,
- },
- `<a onclick='a = &#34;\u003ca\u003e&#34;<b;'>`,
- },
- // A recursive template that ends in its start context.
- {
- map[string]string{
- "main": `{{range .Children}}{{template "main" .}}{{else}}{{.X}} {{end}}`,
- },
- `foo &lt;bar&gt; baz `,
- },
- // A recursive helper template that ends in its start context.
- {
- map[string]string{
- "main": `{{template "helper" .}}`,
- "helper": `{{if .Children}}<ul>{{range .Children}}<li>{{template "main" .}}</li>{{end}}</ul>{{else}}{{.X}}{{end}}`,
- },
- `<ul><li>foo</li><li>&lt;bar&gt;</li><li><ul><li>baz</li></ul></li></ul>`,
- },
- // Co-recursive templates that end in its start context.
- {
- map[string]string{
- "main": `<blockquote>{{range .Children}}{{template "helper" .}}{{end}}</blockquote>`,
- "helper": `{{if .Children}}{{template "main" .}}{{else}}{{.X}}<br>{{end}}`,
- },
- `<blockquote>foo<br>&lt;bar&gt;<br><blockquote>baz<br></blockquote></blockquote>`,
- },
- // A template that is called in two different contexts.
- {
- map[string]string{
- "main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`,
- "helper": `{{11}} of {{"<100>"}}`,
- },
- `<button onclick="title='11 of \x3c100\x3e'; ...">11 of &lt;100&gt;</button>`,
- },
- // A non-recursive template that ends in a different context.
- // helper starts in jsCtxRegexp and ends in jsCtxDivOp.
- {
- map[string]string{
- "main": `<script>var x={{template "helper"}}/{{"42"}};</script>`,
- "helper": "{{126}}",
- },
- `<script>var x= 126 /"42";</script>`,
- },
- // A recursive template that ends in a similar context.
- {
- map[string]string{
- "main": `<script>var x=[{{template "countdown" 4}}];</script>`,
- "countdown": `{{.}}{{if .}},{{template "countdown" . | pred}}{{end}}`,
- },
- `<script>var x=[ 4 , 3 , 2 , 1 , 0 ];</script>`,
- },
- // A recursive template that ends in a different context.
- /*
- {
- map[string]string{
- "main": `<a href="/foo{{template "helper" .}}">`,
- "helper": `{{if .Children}}{{range .Children}}{{template "helper" .}}{{end}}{{else}}?x={{.X}}{{end}}`,
- },
- `<a href="/foo?x=foo?x=%3cbar%3e?x=baz">`,
- },
- */
- }
-
- // pred is a template function that returns the predecessor of a
- // natural number for testing recursive templates.
- fns := FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
- if len(a) == 1 {
- if i, _ := a[0].(int); i > 0 {
- return i - 1, nil
- }
- }
- return nil, fmt.Errorf("undefined pred(%v)", a)
- }}
-
- for _, test := range tests {
- source := ""
- for name, body := range test.inputs {
- source += fmt.Sprintf("{{define %q}}%s{{end}} ", name, body)
- }
- tmpl, err := New("root").Funcs(fns).Parse(source)
- if err != nil {
- t.Errorf("error parsing %q: %v", source, err)
- continue
- }
- var b bytes.Buffer
-
- if err := tmpl.ExecuteTemplate(&b, "main", data); err != nil {
- t.Errorf("%q executing %v", err.Error(), tmpl.Lookup("main"))
- continue
- }
- if got := b.String(); test.want != got {
- t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got)
- }
- }
-
-}
-
-func TestErrors(t *testing.T) {
- tests := []struct {
- input string
- err string
- }{
- // Non-error cases.
- {
- "{{if .Cond}}<a>{{else}}<b>{{end}}",
- "",
- },
- {
- "{{if .Cond}}<a>{{end}}",
- "",
- },
- {
- "{{if .Cond}}{{else}}<b>{{end}}",
- "",
- },
- {
- "{{with .Cond}}<div>{{end}}",
- "",
- },
- {
- "{{range .Items}}<a>{{end}}",
- "",
- },
- {
- "<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>",
- "",
- },
- // Error cases.
- {
- "{{if .Cond}}<a{{end}}",
- "z:1: {{if}} branches",
- },
- {
- "{{if .Cond}}\n{{else}}\n<a{{end}}",
- "z:1: {{if}} branches",
- },
- {
- // Missing quote in the else branch.
- `{{if .Cond}}<a href="foo">{{else}}<a href="bar>{{end}}`,
- "z:1: {{if}} branches",
- },
- {
- // Different kind of attribute: href implies a URL.
- "<a {{if .Cond}}href='{{else}}title='{{end}}{{.X}}'>",
- "z:1: {{if}} branches",
- },
- {
- "\n{{with .X}}<a{{end}}",
- "z:2: {{with}} branches",
- },
- {
- "\n{{with .X}}<a>{{else}}<a{{end}}",
- "z:2: {{with}} branches",
- },
- {
- "{{range .Items}}<a{{end}}",
- `z:1: on range loop re-entry: "<" in attribute name: "<a"`,
- },
- {
- "\n{{range .Items}} x='<a{{end}}",
- "z:2: on range loop re-entry: {{range}} branches",
- },
- {
- "<a b=1 c={{.H}}",
- "z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd",
- },
- {
- "<script>foo();",
- "z: ends in a non-text context: {stateJS",
- },
- {
- `<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`,
- "z:1: {{.H}} appears in an ambiguous URL context",
- },
- {
- `<a onclick="alert('Hello \`,
- `unfinished escape sequence in JS string: "Hello \\"`,
- },
- {
- `<a onclick='alert("Hello\, World\`,
- `unfinished escape sequence in JS string: "Hello\\, World\\"`,
- },
- {
- `<a onclick='alert(/x+\`,
- `unfinished escape sequence in JS string: "x+\\"`,
- },
- {
- `<a onclick="/foo[\]/`,
- `unfinished JS regexp charset: "foo[\\]/"`,
- },
- {
- // It is ambiguous whether 1.5 should be 1\.5 or 1.5.
- // Either `var x = 1/- 1.5 /i.test(x)`
- // where `i.test(x)` is a method call of reference i,
- // or `/-1\.5/i.test(x)` which is a method call on a
- // case insensitive regular expression.
- `<script>{{if false}}var x = 1{{end}}/-{{"1.5"}}/i.test(x)</script>`,
- `'/' could start a division or regexp: "/-"`,
- },
- {
- `{{template "foo"}}`,
- "z:1: no such template \"foo\"",
- },
- {
- `<div{{template "y"}}>` +
- // Illegal starting in stateTag but not in stateText.
- `{{define "y"}} foo<b{{end}}`,
- `"<" in attribute name: " foo<b"`,
- },
- {
- `<script>reverseList = [{{template "t"}}]</script>` +
- // Missing " after recursive call.
- `{{define "t"}}{{if .Tail}}{{template "t" .Tail}}{{end}}{{.Head}}",{{end}}`,
- `: cannot compute output context for template t$htmltemplate_stateJS_elementScript`,
- },
- {
- `<input type=button value=onclick=>`,
- `html/template:z: "=" in unquoted attr: "onclick="`,
- },
- {
- `<input type=button value= onclick=>`,
- `html/template:z: "=" in unquoted attr: "onclick="`,
- },
- {
- `<input type=button value= 1+1=2>`,
- `html/template:z: "=" in unquoted attr: "1+1=2"`,
- },
- {
- "<a class=`foo>",
- "html/template:z: \"`\" in unquoted attr: \"`foo\"",
- },
- {
- `<a style=font:'Arial'>`,
- `html/template:z: "'" in unquoted attr: "font:'Arial'"`,
- },
- {
- `<a=foo>`,
- `: expected space, attr name, or end of tag, but got "=foo>"`,
- },
- }
-
- for _, test := range tests {
- buf := new(bytes.Buffer)
- tmpl, err := New("z").Parse(test.input)
- if err != nil {
- t.Errorf("input=%q: unexpected parse error %s\n", test.input, err)
- continue
- }
- err = tmpl.Execute(buf, nil)
- var got string
- if err != nil {
- got = err.Error()
- }
- if test.err == "" {
- if got != "" {
- t.Errorf("input=%q: unexpected error %q", test.input, got)
- }
- continue
- }
- if strings.Index(got, test.err) == -1 {
- t.Errorf("input=%q: error\n\t%q\ndoes not contain expected string\n\t%q", test.input, got, test.err)
- continue
- }
- }
-}
-
-func TestEscapeText(t *testing.T) {
- tests := []struct {
- input string
- output context
- }{
- {
- ``,
- context{},
- },
- {
- `Hello, World!`,
- context{},
- },
- {
- // An orphaned "<" is OK.
- `I <3 Ponies!`,
- context{},
- },
- {
- `<a`,
- context{state: stateTag},
- },
- {
- `<a `,
- context{state: stateTag},
- },
- {
- `<a>`,
- context{state: stateText},
- },
- {
- `<a href`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a on`,
- context{state: stateAttrName, attr: attrScript},
- },
- {
- `<a href `,
- context{state: stateAfterName, attr: attrURL},
- },
- {
- `<a style = `,
- context{state: stateBeforeValue, attr: attrStyle},
- },
- {
- `<a href=`,
- context{state: stateBeforeValue, attr: attrURL},
- },
- {
- `<a href=x`,
- context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
- },
- {
- `<a href=x `,
- context{state: stateTag},
- },
- {
- `<a href=>`,
- context{state: stateText},
- },
- {
- `<a href=x>`,
- context{state: stateText},
- },
- {
- `<a href ='`,
- context{state: stateURL, delim: delimSingleQuote},
- },
- {
- `<a href=''`,
- context{state: stateTag},
- },
- {
- `<a href= "`,
- context{state: stateURL, delim: delimDoubleQuote},
- },
- {
- `<a href=""`,
- context{state: stateTag},
- },
- {
- `<a title="`,
- context{state: stateAttr, delim: delimDoubleQuote},
- },
- {
- `<a HREF='http:`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a Href='/`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href='"`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href="'`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href='&apos;`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href="&quot;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href="&#34;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a href=&quot;`,
- context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
- },
- {
- `<img alt="1">`,
- context{state: stateText},
- },
- {
- `<img alt="1>"`,
- context{state: stateTag},
- },
- {
- `<img alt="1>">`,
- context{state: stateText},
- },
- {
- `<input checked type="checkbox"`,
- context{state: stateTag},
- },
- {
- `<a onclick="`,
- context{state: stateJS, delim: delimDoubleQuote},
- },
- {
- `<a onclick="//foo`,
- context{state: stateJSLineCmt, delim: delimDoubleQuote},
- },
- {
- "<a onclick='//\n",
- context{state: stateJS, delim: delimSingleQuote},
- },
- {
- "<a onclick='//\r\n",
- context{state: stateJS, delim: delimSingleQuote},
- },
- {
- "<a onclick='//\u2028",
- context{state: stateJS, delim: delimSingleQuote},
- },
- {
- `<a onclick="/*`,
- context{state: stateJSBlockCmt, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/*/`,
- context{state: stateJSBlockCmt, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/**/`,
- context{state: stateJS, delim: delimDoubleQuote},
- },
- {
- `<a onkeypress="&quot;`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
- },
- {
- `<a onclick='&quot;foo&quot;`,
- context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<a onclick=&#39;foo&#39;`,
- context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp},
- },
- {
- `<a onclick=&#39;foo`,
- context{state: stateJSSqStr, delim: delimSpaceOrTagEnd},
- },
- {
- `<a onclick="&quot;foo'`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
- },
- {
- `<a onclick="'foo&quot;`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
- },
- {
- `<A ONCLICK="'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
- },
- {
- `<a onclick="'foo'`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<a onclick="'foo\'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
- },
- {
- `<a onclick="'foo\'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/foo/`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<script>/foo/ /=`,
- context{state: stateJS, element: elementScript},
- },
- {
- `<a onclick="1 /foo`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<a onclick="1 /*c*/ /foo`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<a onclick="/foo[/]`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/foo\/`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
- },
- {
- `<a onclick="/foo/`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
- },
- {
- `<input checked style="`,
- context{state: stateCSS, delim: delimDoubleQuote},
- },
- {
- `<a style="//`,
- context{state: stateCSSLineCmt, delim: delimDoubleQuote},
- },
- {
- `<a style="//</script>`,
- context{state: stateCSSLineCmt, delim: delimDoubleQuote},
- },
- {
- "<a style='//\n",
- context{state: stateCSS, delim: delimSingleQuote},
- },
- {
- "<a style='//\r",
- context{state: stateCSS, delim: delimSingleQuote},
- },
- {
- `<a style="/*`,
- context{state: stateCSSBlockCmt, delim: delimDoubleQuote},
- },
- {
- `<a style="/*/`,
- context{state: stateCSSBlockCmt, delim: delimDoubleQuote},
- },
- {
- `<a style="/**/`,
- context{state: stateCSS, delim: delimDoubleQuote},
- },
- {
- `<a style="background: '`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote},
- },
- {
- `<a style="background: &quot;`,
- context{state: stateCSSDqStr, delim: delimDoubleQuote},
- },
- {
- `<a style="background: '/foo?img=`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag},
- },
- {
- `<a style="background: '/`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url(&#x22;/`,
- context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url('/`,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url('/)`,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url('/ `,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url(/`,
- context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
- },
- {
- `<a style="background: url( `,
- context{state: stateCSSURL, delim: delimDoubleQuote},
- },
- {
- `<a style="background: url( /image?name=`,
- context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag},
- },
- {
- `<a style="background: url(x)`,
- context{state: stateCSS, delim: delimDoubleQuote},
- },
- {
- `<a style="background: url('x'`,
- context{state: stateCSS, delim: delimDoubleQuote},
- },
- {
- `<a style="background: url( x `,
- context{state: stateCSS, delim: delimDoubleQuote},
- },
- {
- `<!-- foo`,
- context{state: stateHTMLCmt},
- },
- {
- `<!-->`,
- context{state: stateHTMLCmt},
- },
- {
- `<!--->`,
- context{state: stateHTMLCmt},
- },
- {
- `<!-- foo -->`,
- context{state: stateText},
- },
- {
- `<script`,
- context{state: stateTag, element: elementScript},
- },
- {
- `<script `,
- context{state: stateTag, element: elementScript},
- },
- {
- `<script src="foo.js" `,
- context{state: stateTag, element: elementScript},
- },
- {
- `<script src='foo.js' `,
- context{state: stateTag, element: elementScript},
- },
- {
- `<script type=text/javascript `,
- context{state: stateTag, element: elementScript},
- },
- {
- `<script>foo`,
- context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript},
- },
- {
- `<script>foo</script>`,
- context{state: stateText},
- },
- {
- `<script>foo</script><!--`,
- context{state: stateHTMLCmt},
- },
- {
- `<script>document.write("<p>foo</p>");`,
- context{state: stateJS, element: elementScript},
- },
- {
- `<script>document.write("<p>foo<\/script>");`,
- context{state: stateJS, element: elementScript},
- },
- {
- `<script>document.write("<script>alert(1)</script>");`,
- context{state: stateText},
- },
- {
- `<Script>`,
- context{state: stateJS, element: elementScript},
- },
- {
- `<SCRIPT>foo`,
- context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript},
- },
- {
- `<textarea>value`,
- context{state: stateRCDATA, element: elementTextarea},
- },
- {
- `<textarea>value</TEXTAREA>`,
- context{state: stateText},
- },
- {
- `<textarea name=html><b`,
- context{state: stateRCDATA, element: elementTextarea},
- },
- {
- `<title>value`,
- context{state: stateRCDATA, element: elementTitle},
- },
- {
- `<style>value`,
- context{state: stateCSS, element: elementStyle},
- },
- {
- `<a xlink:href`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a xmlns`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a xmlns:foo`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a xmlnsxyz`,
- context{state: stateAttrName},
- },
- {
- `<a data-url`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a data-iconUri`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a data-urlItem`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a g:`,
- context{state: stateAttrName},
- },
- {
- `<a g:url`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a g:iconUri`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a g:urlItem`,
- context{state: stateAttrName, attr: attrURL},
- },
- {
- `<a g:value`,
- context{state: stateAttrName},
- },
- {
- `<a svg:style='`,
- context{state: stateCSS, delim: delimSingleQuote},
- },
- {
- `<svg:font-face`,
- context{state: stateTag},
- },
- {
- `<svg:a svg:onclick="`,
- context{state: stateJS, delim: delimDoubleQuote},
- },
- }
-
- for _, test := range tests {
- b, e := []byte(test.input), newEscaper(nil)
- c := e.escapeText(context{}, &parse.TextNode{NodeType: parse.NodeText, Text: b})
- if !test.output.eq(c) {
- t.Errorf("input %q: want context\n\t%v\ngot\n\t%v", test.input, test.output, c)
- continue
- }
- if test.input != string(b) {
- t.Errorf("input %q: text node was modified: want %q got %q", test.input, test.input, b)
- continue
- }
- }
-}
-
-func TestEnsurePipelineContains(t *testing.T) {
- tests := []struct {
- input, output string
- ids []string
- }{
- {
- "{{.X}}",
- ".X",
- []string{},
- },
- {
- "{{.X | html}}",
- ".X | html",
- []string{},
- },
- {
- "{{.X}}",
- ".X | html",
- []string{"html"},
- },
- {
- "{{.X | html}}",
- ".X | html | urlquery",
- []string{"urlquery"},
- },
- {
- "{{.X | html | urlquery}}",
- ".X | html | urlquery",
- []string{"urlquery"},
- },
- {
- "{{.X | html | urlquery}}",
- ".X | html | urlquery",
- []string{"html", "urlquery"},
- },
- {
- "{{.X | html | urlquery}}",
- ".X | html | urlquery",
- []string{"html"},
- },
- {
- "{{.X | urlquery}}",
- ".X | html | urlquery",
- []string{"html", "urlquery"},
- },
- {
- "{{.X | html | print}}",
- ".X | urlquery | html | print",
- []string{"urlquery", "html"},
- },
- {
- "{{($).X | html | print}}",
- "($).X | urlquery | html | print",
- []string{"urlquery", "html"},
- },
- }
- for i, test := range tests {
- tmpl := template.Must(template.New("test").Parse(test.input))
- action, ok := (tmpl.Tree.Root.Nodes[0].(*parse.ActionNode))
- if !ok {
- t.Errorf("#%d: First node is not an action: %s", i, test.input)
- continue
- }
- pipe := action.Pipe
- ensurePipelineContains(pipe, test.ids)
- got := pipe.String()
- if got != test.output {
- t.Errorf("#%d: %s, %v: want\n\t%s\ngot\n\t%s", i, test.input, test.ids, test.output, got)
- }
- }
-}
-
-func TestEscapeErrorsNotIgnorable(t *testing.T) {
- var b bytes.Buffer
- tmpl, _ := New("dangerous").Parse("<a")
- err := tmpl.Execute(&b, nil)
- if err == nil {
- t.Errorf("Expected error")
- } else if b.Len() != 0 {
- t.Errorf("Emitted output despite escaping failure")
- }
-}
-
-func TestEscapeSetErrorsNotIgnorable(t *testing.T) {
- var b bytes.Buffer
- tmpl, err := New("root").Parse(`{{define "t"}}<a{{end}}`)
- if err != nil {
- t.Errorf("failed to parse set: %q", err)
- }
- err = tmpl.ExecuteTemplate(&b, "t", nil)
- if err == nil {
- t.Errorf("Expected error")
- } else if b.Len() != 0 {
- t.Errorf("Emitted output despite escaping failure")
- }
-}
-
-func TestRedundantFuncs(t *testing.T) {
- inputs := []interface{}{
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !"#$%&'()*+,-./` +
- `0123456789:;<=>?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\ufdec\ufffd\uffff\U0001D11E" +
- "&amp;%22\\",
- CSS(`a[href =~ "//example.com"]#foo`),
- HTML(`Hello, <b>World</b> &amp;tc!`),
- HTMLAttr(` dir="ltr"`),
- JS(`c && alert("Hello, World!");`),
- JSStr(`Hello, World & O'Reilly\x21`),
- URL(`greeting=H%69&addressee=(World)`),
- }
-
- for n0, m := range redundantFuncs {
- f0 := funcMap[n0].(func(...interface{}) string)
- for n1 := range m {
- f1 := funcMap[n1].(func(...interface{}) string)
- for _, input := range inputs {
- want := f0(input)
- if got := f1(want); want != got {
- t.Errorf("%s %s with %T %q: want\n\t%q,\ngot\n\t%q", n0, n1, input, input, want, got)
- }
- }
- }
- }
-}
-
-func TestIndirectPrint(t *testing.T) {
- a := 3
- ap := &a
- b := "hello"
- bp := &b
- bpp := &bp
- tmpl := Must(New("t").Parse(`{{.}}`))
- var buf bytes.Buffer
- err := tmpl.Execute(&buf, ap)
- if err != nil {
- t.Errorf("Unexpected error: %s", err)
- } else if buf.String() != "3" {
- t.Errorf(`Expected "3"; got %q`, buf.String())
- }
- buf.Reset()
- err = tmpl.Execute(&buf, bpp)
- if err != nil {
- t.Errorf("Unexpected error: %s", err)
- } else if buf.String() != "hello" {
- t.Errorf(`Expected "hello"; got %q`, buf.String())
- }
-}
-
-// This is a test for issue 3272.
-func TestEmptyTemplate(t *testing.T) {
- page := Must(New("page").ParseFiles(os.DevNull))
- if err := page.ExecuteTemplate(os.Stdout, "page", "nothing"); err == nil {
- t.Fatal("expected error")
- }
-}
-
-type Issue7379 int
-
-func (Issue7379) SomeMethod(x int) string {
- return fmt.Sprintf("<%d>", x)
-}
-
-// This is a test for issue 7379: type assertion error caused panic, and then
-// the code to handle the panic breaks escaping. It's hard to see the second
-// problem once the first is fixed, but its fix is trivial so we let that go. See
-// the discussion for issue 7379.
-func TestPipeToMethodIsEscaped(t *testing.T) {
- tmpl := Must(New("x").Parse("<html>{{0 | .SomeMethod}}</html>\n"))
- tryExec := func() string {
- defer func() {
- panicValue := recover()
- if panicValue != nil {
- t.Errorf("panicked: %v\n", panicValue)
- }
- }()
- var b bytes.Buffer
- tmpl.Execute(&b, Issue7379(0))
- return b.String()
- }
- for i := 0; i < 3; i++ {
- str := tryExec()
- const expect = "<html>&lt;0&gt;</html>\n"
- if str != expect {
- t.Errorf("expected %q got %q", expect, str)
- }
- }
-}
-
-func BenchmarkEscapedExecute(b *testing.B) {
- tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`))
- var buf bytes.Buffer
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- tmpl.Execute(&buf, "foo & 'bar' & baz")
- buf.Reset()
- }
-}
diff --git a/src/pkg/html/template/html.go b/src/pkg/html/template/html.go
deleted file mode 100644
index 9c069efd1..000000000
--- a/src/pkg/html/template/html.go
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "strings"
- "unicode/utf8"
-)
-
-// htmlNospaceEscaper escapes for inclusion in unquoted attribute values.
-func htmlNospaceEscaper(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeHTML {
- return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false)
- }
- return htmlReplacer(s, htmlNospaceReplacementTable, false)
-}
-
-// attrEscaper escapes for inclusion in quoted attribute values.
-func attrEscaper(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeHTML {
- return htmlReplacer(stripTags(s), htmlNormReplacementTable, true)
- }
- return htmlReplacer(s, htmlReplacementTable, true)
-}
-
-// rcdataEscaper escapes for inclusion in an RCDATA element body.
-func rcdataEscaper(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeHTML {
- return htmlReplacer(s, htmlNormReplacementTable, true)
- }
- return htmlReplacer(s, htmlReplacementTable, true)
-}
-
-// htmlEscaper escapes for inclusion in HTML text.
-func htmlEscaper(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeHTML {
- return s
- }
- return htmlReplacer(s, htmlReplacementTable, true)
-}
-
-// htmlReplacementTable contains the runes that need to be escaped
-// inside a quoted attribute value or in a text node.
-var htmlReplacementTable = []string{
- // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
- // U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT
- // CHARACTER character to the current attribute's value.
- // "
- // and similarly
- // http://www.w3.org/TR/html5/syntax.html#before-attribute-value-state
- 0: "\uFFFD",
- '"': "&#34;",
- '&': "&amp;",
- '\'': "&#39;",
- '+': "&#43;",
- '<': "&lt;",
- '>': "&gt;",
-}
-
-// htmlNormReplacementTable is like htmlReplacementTable but without '&' to
-// avoid over-encoding existing entities.
-var htmlNormReplacementTable = []string{
- 0: "\uFFFD",
- '"': "&#34;",
- '\'': "&#39;",
- '+': "&#43;",
- '<': "&lt;",
- '>': "&gt;",
-}
-
-// htmlNospaceReplacementTable contains the runes that need to be escaped
-// inside an unquoted attribute value.
-// The set of runes escaped is the union of the HTML specials and
-// those determined by running the JS below in browsers:
-// <div id=d></div>
-// <script>(function () {
-// var a = [], d = document.getElementById("d"), i, c, s;
-// for (i = 0; i < 0x10000; ++i) {
-// c = String.fromCharCode(i);
-// d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
-// s = d.getElementsByTagName("SPAN")[0];
-// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
-// }
-// document.write(a.join(", "));
-// })()</script>
-var htmlNospaceReplacementTable = []string{
- 0: "&#xfffd;",
- '\t': "&#9;",
- '\n': "&#10;",
- '\v': "&#11;",
- '\f': "&#12;",
- '\r': "&#13;",
- ' ': "&#32;",
- '"': "&#34;",
- '&': "&amp;",
- '\'': "&#39;",
- '+': "&#43;",
- '<': "&lt;",
- '=': "&#61;",
- '>': "&gt;",
- // A parse error in the attribute value (unquoted) and
- // before attribute value states.
- // Treated as a quoting character by IE.
- '`': "&#96;",
-}
-
-// htmlNospaceNormReplacementTable is like htmlNospaceReplacementTable but
-// without '&' to avoid over-encoding existing entities.
-var htmlNospaceNormReplacementTable = []string{
- 0: "&#xfffd;",
- '\t': "&#9;",
- '\n': "&#10;",
- '\v': "&#11;",
- '\f': "&#12;",
- '\r': "&#13;",
- ' ': "&#32;",
- '"': "&#34;",
- '\'': "&#39;",
- '+': "&#43;",
- '<': "&lt;",
- '=': "&#61;",
- '>': "&gt;",
- // A parse error in the attribute value (unquoted) and
- // before attribute value states.
- // Treated as a quoting character by IE.
- '`': "&#96;",
-}
-
-// htmlReplacer returns s with runes replaced according to replacementTable
-// and when badRunes is true, certain bad runes are allowed through unescaped.
-func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
- written, b := 0, new(bytes.Buffer)
- for i, r := range s {
- if int(r) < len(replacementTable) {
- if repl := replacementTable[r]; len(repl) != 0 {
- b.WriteString(s[written:i])
- b.WriteString(repl)
- // Valid as long as replacementTable doesn't
- // include anything above 0x7f.
- written = i + utf8.RuneLen(r)
- }
- } else if badRunes {
- // No-op.
- // IE does not allow these ranges in unquoted attrs.
- } else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff {
- fmt.Fprintf(b, "%s&#x%x;", s[written:i], r)
- written = i + utf8.RuneLen(r)
- }
- }
- if written == 0 {
- return s
- }
- b.WriteString(s[written:])
- return b.String()
-}
-
-// stripTags takes a snippet of HTML and returns only the text content.
-// For example, `<b>&iexcl;Hi!</b> <script>...</script>` -> `&iexcl;Hi! `.
-func stripTags(html string) string {
- var b bytes.Buffer
- s, c, i, allText := []byte(html), context{}, 0, true
- // Using the transition funcs helps us avoid mangling
- // `<div title="1>2">` or `I <3 Ponies!`.
- for i != len(s) {
- if c.delim == delimNone {
- st := c.state
- // Use RCDATA instead of parsing into JS or CSS styles.
- if c.element != elementNone && !isInTag(st) {
- st = stateRCDATA
- }
- d, nread := transitionFunc[st](c, s[i:])
- i1 := i + nread
- if c.state == stateText || c.state == stateRCDATA {
- // Emit text up to the start of the tag or comment.
- j := i1
- if d.state != c.state {
- for j1 := j - 1; j1 >= i; j1-- {
- if s[j1] == '<' {
- j = j1
- break
- }
- }
- }
- b.Write(s[i:j])
- } else {
- allText = false
- }
- c, i = d, i1
- continue
- }
- i1 := i + bytes.IndexAny(s[i:], delimEnds[c.delim])
- if i1 < i {
- break
- }
- if c.delim != delimSpaceOrTagEnd {
- // Consume any quote.
- i1++
- }
- c, i = context{state: stateTag, element: c.element}, i1
- }
- if allText {
- return html
- } else if c.state == stateText || c.state == stateRCDATA {
- b.Write(s[i:])
- }
- return b.String()
-}
-
-// htmlNameFilter accepts valid parts of an HTML attribute or tag name or
-// a known-safe HTML attribute.
-func htmlNameFilter(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeHTMLAttr {
- return s
- }
- if len(s) == 0 {
- // Avoid violation of structure preservation.
- // <input checked {{.K}}={{.V}}>.
- // Without this, if .K is empty then .V is the value of
- // checked, but otherwise .V is the value of the attribute
- // named .K.
- return filterFailsafe
- }
- s = strings.ToLower(s)
- if t := attrType(s); t != contentTypePlain {
- // TODO: Split attr and element name part filters so we can whitelist
- // attributes.
- return filterFailsafe
- }
- for _, r := range s {
- switch {
- case '0' <= r && r <= '9':
- case 'a' <= r && r <= 'z':
- default:
- return filterFailsafe
- }
- }
- return s
-}
-
-// commentEscaper returns the empty string regardless of input.
-// Comment content does not correspond to any parsed structure or
-// human-readable content, so the simplest and most secure policy is to drop
-// content interpolated into comments.
-// This approach is equally valid whether or not static comment content is
-// removed from the template.
-func commentEscaper(args ...interface{}) string {
- return ""
-}
diff --git a/src/pkg/html/template/html_test.go b/src/pkg/html/template/html_test.go
deleted file mode 100644
index b9b970387..000000000
--- a/src/pkg/html/template/html_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "html"
- "strings"
- "testing"
-)
-
-func TestHTMLNospaceEscaper(t *testing.T) {
- input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !"#$%&'()*+,-./` +
- `0123456789:;<=>?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E")
-
- want := ("&#xfffd;\x01\x02\x03\x04\x05\x06\x07" +
- "\x08&#9;&#10;&#11;&#12;&#13;\x0E\x0F" +
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- `&#32;!&#34;#$%&amp;&#39;()*&#43;,-./` +
- `0123456789:;&lt;&#61;&gt;?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- `&#96;abcdefghijklmno` +
- `pqrstuvwxyz{|}~` + "\u007f" +
- "\u00A0\u0100\u2028\u2029\ufeff&#xfdec;\U0001D11E")
-
- got := htmlNospaceEscaper(input)
- if got != want {
- t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got)
- }
-
- got, want = html.UnescapeString(got), strings.Replace(input, "\x00", "\ufffd", 1)
- if want != got {
- t.Errorf("decode: want\n\t%q\nbut got\n\t%q", want, got)
- }
-}
-
-func TestStripTags(t *testing.T) {
- tests := []struct {
- input, want string
- }{
- {"", ""},
- {"Hello, World!", "Hello, World!"},
- {"foo&amp;bar", "foo&amp;bar"},
- {`Hello <a href="www.example.com/">World</a>!`, "Hello World!"},
- {"Foo <textarea>Bar</textarea> Baz", "Foo Bar Baz"},
- {"Foo <!-- Bar --> Baz", "Foo Baz"},
- {"<", "<"},
- {"foo < bar", "foo < bar"},
- {`Foo<script type="text/javascript">alert(1337)</script>Bar`, "FooBar"},
- {`Foo<div title="1>2">Bar`, "FooBar"},
- {`I <3 Ponies!`, `I <3 Ponies!`},
- {`<script>foo()</script>`, ``},
- }
-
- for _, test := range tests {
- if got := stripTags(test.input); got != test.want {
- t.Errorf("%q: want %q, got %q", test.input, test.want, got)
- }
- }
-}
-
-func BenchmarkHTMLNospaceEscaper(b *testing.B) {
- for i := 0; i < b.N; i++ {
- htmlNospaceEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
-
-func BenchmarkHTMLNospaceEscaperNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- htmlNospaceEscaper("The_quick,_brown_fox_jumps_over_the_lazy_dog.")
- }
-}
-
-func BenchmarkStripTags(b *testing.B) {
- for i := 0; i < b.N; i++ {
- stripTags("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
-
-func BenchmarkStripTagsNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- stripTags("The quick, brown fox jumps over the lazy dog.")
- }
-}
diff --git a/src/pkg/html/template/js.go b/src/pkg/html/template/js.go
deleted file mode 100644
index 999a61ed0..000000000
--- a/src/pkg/html/template/js.go
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "reflect"
- "strings"
- "unicode/utf8"
-)
-
-// nextJSCtx returns the context that determines whether a slash after the
-// given run of tokens starts a regular expression instead of a division
-// operator: / or /=.
-//
-// This assumes that the token run does not include any string tokens, comment
-// tokens, regular expression literal tokens, or division operators.
-//
-// This fails on some valid but nonsensical JavaScript programs like
-// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to
-// fail on any known useful programs. It is based on the draft
-// JavaScript 2.0 lexical grammar and requires one token of lookbehind:
-// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html
-func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
- s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029")
- if len(s) == 0 {
- return preceding
- }
-
- // All cases below are in the single-byte UTF-8 group.
- switch c, n := s[len(s)-1], len(s); c {
- case '+', '-':
- // ++ and -- are not regexp preceders, but + and - are whether
- // they are used as infix or prefix operators.
- start := n - 1
- // Count the number of adjacent dashes or pluses.
- for start > 0 && s[start-1] == c {
- start--
- }
- if (n-start)&1 == 1 {
- // Reached for trailing minus signs since "---" is the
- // same as "-- -".
- return jsCtxRegexp
- }
- return jsCtxDivOp
- case '.':
- // Handle "42."
- if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' {
- return jsCtxDivOp
- }
- return jsCtxRegexp
- // Suffixes for all punctuators from section 7.7 of the language spec
- // that only end binary operators not handled above.
- case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?':
- return jsCtxRegexp
- // Suffixes for all punctuators from section 7.7 of the language spec
- // that are prefix operators not handled above.
- case '!', '~':
- return jsCtxRegexp
- // Matches all the punctuators from section 7.7 of the language spec
- // that are open brackets not handled above.
- case '(', '[':
- return jsCtxRegexp
- // Matches all the punctuators from section 7.7 of the language spec
- // that precede expression starts.
- case ':', ';', '{':
- return jsCtxRegexp
- // CAVEAT: the close punctuators ('}', ']', ')') precede div ops and
- // are handled in the default except for '}' which can precede a
- // division op as in
- // ({ valueOf: function () { return 42 } } / 2
- // which is valid, but, in practice, developers don't divide object
- // literals, so our heuristic works well for code like
- // function () { ... } /foo/.test(x) && sideEffect();
- // The ')' punctuator can precede a regular expression as in
- // if (b) /foo/.test(x) && ...
- // but this is much less likely than
- // (a + b) / c
- case '}':
- return jsCtxRegexp
- default:
- // Look for an IdentifierName and see if it is a keyword that
- // can precede a regular expression.
- j := n
- for j > 0 && isJSIdentPart(rune(s[j-1])) {
- j--
- }
- if regexpPrecederKeywords[string(s[j:])] {
- return jsCtxRegexp
- }
- }
- // Otherwise is a punctuator not listed above, or
- // a string which precedes a div op, or an identifier
- // which precedes a div op.
- return jsCtxDivOp
-}
-
-// regexpPrecederKeywords is a set of reserved JS keywords that can precede a
-// regular expression in JS source.
-var regexpPrecederKeywords = map[string]bool{
- "break": true,
- "case": true,
- "continue": true,
- "delete": true,
- "do": true,
- "else": true,
- "finally": true,
- "in": true,
- "instanceof": true,
- "return": true,
- "throw": true,
- "try": true,
- "typeof": true,
- "void": true,
-}
-
-var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
-
-// indirectToJSONMarshaler returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
-func indirectToJSONMarshaler(a interface{}) interface{} {
- v := reflect.ValueOf(a)
- for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
-// neither side-effects nor free variables outside (NaN, Infinity).
-func jsValEscaper(args ...interface{}) string {
- var a interface{}
- if len(args) == 1 {
- a = indirectToJSONMarshaler(args[0])
- switch t := a.(type) {
- case JS:
- return string(t)
- case JSStr:
- // TODO: normalize quotes.
- return `"` + string(t) + `"`
- case json.Marshaler:
- // Do not treat as a Stringer.
- case fmt.Stringer:
- a = t.String()
- }
- } else {
- for i, arg := range args {
- args[i] = indirectToJSONMarshaler(arg)
- }
- a = fmt.Sprint(args...)
- }
- // TODO: detect cycles before calling Marshal which loops infinitely on
- // cyclic data. This may be an unacceptable DoS risk.
-
- b, err := json.Marshal(a)
- if err != nil {
- // Put a space before comment so that if it is flush against
- // a division operator it is not turned into a line comment:
- // x/{{y}}
- // turning into
- // x//* error marshalling y:
- // second line of error message */null
- return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
- }
-
- // TODO: maybe post-process output to prevent it from containing
- // "<!--", "-->", "<![CDATA[", "]]>", or "</script"
- // in case custom marshallers produce output containing those.
-
- // TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
- if len(b) == 0 {
- // In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
- // not cause the output `x=y/*z`.
- return " null "
- }
- first, _ := utf8.DecodeRune(b)
- last, _ := utf8.DecodeLastRune(b)
- var buf bytes.Buffer
- // Prevent IdentifierNames and NumericLiterals from running into
- // keywords: in, instanceof, typeof, void
- pad := isJSIdentPart(first) || isJSIdentPart(last)
- if pad {
- buf.WriteByte(' ')
- }
- written := 0
- // Make sure that json.Marshal escapes codepoints U+2028 & U+2029
- // so it falls within the subset of JSON which is valid JS.
- for i := 0; i < len(b); {
- rune, n := utf8.DecodeRune(b[i:])
- repl := ""
- if rune == 0x2028 {
- repl = `\u2028`
- } else if rune == 0x2029 {
- repl = `\u2029`
- }
- if repl != "" {
- buf.Write(b[written:i])
- buf.WriteString(repl)
- written = i + n
- }
- i += n
- }
- if buf.Len() != 0 {
- buf.Write(b[written:])
- if pad {
- buf.WriteByte(' ')
- }
- b = buf.Bytes()
- }
- return string(b)
-}
-
-// jsStrEscaper produces a string that can be included between quotes in
-// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
-// or in an HTML5 event handler attribute such as onclick.
-func jsStrEscaper(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeJSStr {
- return replace(s, jsStrNormReplacementTable)
- }
- return replace(s, jsStrReplacementTable)
-}
-
-// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
-// specials so the result is treated literally when included in a regular
-// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
-// the literal text of {{.X}} followed by the string "bar".
-func jsRegexpEscaper(args ...interface{}) string {
- s, _ := stringify(args...)
- s = replace(s, jsRegexpReplacementTable)
- if s == "" {
- // /{{.X}}/ should not produce a line comment when .X == "".
- return "(?:)"
- }
- return s
-}
-
-// replace replaces each rune r of s with replacementTable[r], provided that
-// r < len(replacementTable). If replacementTable[r] is the empty string then
-// no replacement is made.
-// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and
-// `\u2029`.
-func replace(s string, replacementTable []string) string {
- var b bytes.Buffer
- written := 0
- for i, r := range s {
- var repl string
- switch {
- case int(r) < len(replacementTable) && replacementTable[r] != "":
- repl = replacementTable[r]
- case r == '\u2028':
- repl = `\u2028`
- case r == '\u2029':
- repl = `\u2029`
- default:
- continue
- }
- b.WriteString(s[written:i])
- b.WriteString(repl)
- written = i + utf8.RuneLen(r)
- }
- if written == 0 {
- return s
- }
- b.WriteString(s[written:])
- return b.String()
-}
-
-var jsStrReplacementTable = []string{
- 0: `\0`,
- '\t': `\t`,
- '\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
- '\f': `\f`,
- '\r': `\r`,
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- '"': `\x22`,
- '&': `\x26`,
- '\'': `\x27`,
- '+': `\x2b`,
- '/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
- '\\': `\\`,
-}
-
-// jsStrNormReplacementTable is like jsStrReplacementTable but does not
-// overencode existing escapes since this table has no entry for `\`.
-var jsStrNormReplacementTable = []string{
- 0: `\0`,
- '\t': `\t`,
- '\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
- '\f': `\f`,
- '\r': `\r`,
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- '"': `\x22`,
- '&': `\x26`,
- '\'': `\x27`,
- '+': `\x2b`,
- '/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
-}
-
-var jsRegexpReplacementTable = []string{
- 0: `\0`,
- '\t': `\t`,
- '\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
- '\f': `\f`,
- '\r': `\r`,
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- '"': `\x22`,
- '$': `\$`,
- '&': `\x26`,
- '\'': `\x27`,
- '(': `\(`,
- ')': `\)`,
- '*': `\*`,
- '+': `\x2b`,
- '-': `\-`,
- '.': `\.`,
- '/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
- '?': `\?`,
- '[': `\[`,
- '\\': `\\`,
- ']': `\]`,
- '^': `\^`,
- '{': `\{`,
- '|': `\|`,
- '}': `\}`,
-}
-
-// isJSIdentPart reports whether the given rune is a JS identifier part.
-// It does not handle all the non-Latin letters, joiners, and combining marks,
-// but it does handle every codepoint that can occur in a numeric literal or
-// a keyword.
-func isJSIdentPart(r rune) bool {
- switch {
- case r == '$':
- return true
- case '0' <= r && r <= '9':
- return true
- case 'A' <= r && r <= 'Z':
- return true
- case r == '_':
- return true
- case 'a' <= r && r <= 'z':
- return true
- }
- return false
-}
diff --git a/src/pkg/html/template/js_test.go b/src/pkg/html/template/js_test.go
deleted file mode 100644
index 311e1d2c4..000000000
--- a/src/pkg/html/template/js_test.go
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "math"
- "strings"
- "testing"
-)
-
-func TestNextJsCtx(t *testing.T) {
- tests := []struct {
- jsCtx jsCtx
- s string
- }{
- // Statement terminators precede regexps.
- {jsCtxRegexp, ";"},
- // This is not airtight.
- // ({ valueOf: function () { return 1 } } / 2)
- // is valid JavaScript but in practice, devs do not do this.
- // A block followed by a statement starting with a RegExp is
- // much more common:
- // while (x) {...} /foo/.test(x) || panic()
- {jsCtxRegexp, "}"},
- // But member, call, grouping, and array expression terminators
- // precede div ops.
- {jsCtxDivOp, ")"},
- {jsCtxDivOp, "]"},
- // At the start of a primary expression, array, or expression
- // statement, expect a regexp.
- {jsCtxRegexp, "("},
- {jsCtxRegexp, "["},
- {jsCtxRegexp, "{"},
- // Assignment operators precede regexps as do all exclusively
- // prefix and binary operators.
- {jsCtxRegexp, "="},
- {jsCtxRegexp, "+="},
- {jsCtxRegexp, "*="},
- {jsCtxRegexp, "*"},
- {jsCtxRegexp, "!"},
- // Whether the + or - is infix or prefix, it cannot precede a
- // div op.
- {jsCtxRegexp, "+"},
- {jsCtxRegexp, "-"},
- // An incr/decr op precedes a div operator.
- // This is not airtight. In (g = ++/h/i) a regexp follows a
- // pre-increment operator, but in practice devs do not try to
- // increment or decrement regular expressions.
- // (g++/h/i) where ++ is a postfix operator on g is much more
- // common.
- {jsCtxDivOp, "--"},
- {jsCtxDivOp, "++"},
- {jsCtxDivOp, "x--"},
- // When we have many dashes or pluses, then they are grouped
- // left to right.
- {jsCtxRegexp, "x---"}, // A postfix -- then a -.
- // return followed by a slash returns the regexp literal or the
- // slash starts a regexp literal in an expression statement that
- // is dead code.
- {jsCtxRegexp, "return"},
- {jsCtxRegexp, "return "},
- {jsCtxRegexp, "return\t"},
- {jsCtxRegexp, "return\n"},
- {jsCtxRegexp, "return\u2028"},
- // Identifiers can be divided and cannot validly be preceded by
- // a regular expressions. Semicolon insertion cannot happen
- // between an identifier and a regular expression on a new line
- // because the one token lookahead for semicolon insertion has
- // to conclude that it could be a div binary op and treat it as
- // such.
- {jsCtxDivOp, "x"},
- {jsCtxDivOp, "x "},
- {jsCtxDivOp, "x\t"},
- {jsCtxDivOp, "x\n"},
- {jsCtxDivOp, "x\u2028"},
- {jsCtxDivOp, "preturn"},
- // Numbers precede div ops.
- {jsCtxDivOp, "0"},
- // Dots that are part of a number are div preceders.
- {jsCtxDivOp, "0."},
- }
-
- for _, test := range tests {
- if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx {
- t.Errorf("want %s got %q", test.jsCtx, test.s)
- }
- if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx {
- t.Errorf("want %s got %q", test.jsCtx, test.s)
- }
- }
-
- if nextJSCtx([]byte(" "), jsCtxRegexp) != jsCtxRegexp {
- t.Error("Blank tokens")
- }
-
- if nextJSCtx([]byte(" "), jsCtxDivOp) != jsCtxDivOp {
- t.Error("Blank tokens")
- }
-}
-
-func TestJSValEscaper(t *testing.T) {
- tests := []struct {
- x interface{}
- js string
- }{
- {int(42), " 42 "},
- {uint(42), " 42 "},
- {int16(42), " 42 "},
- {uint16(42), " 42 "},
- {int32(-42), " -42 "},
- {uint32(42), " 42 "},
- {int16(-42), " -42 "},
- {uint16(42), " 42 "},
- {int64(-42), " -42 "},
- {uint64(42), " 42 "},
- {uint64(1) << 53, " 9007199254740992 "},
- // ulp(1 << 53) > 1 so this loses precision in JS
- // but it is still a representable integer literal.
- {uint64(1)<<53 + 1, " 9007199254740993 "},
- {float32(1.0), " 1 "},
- {float32(-1.0), " -1 "},
- {float32(0.5), " 0.5 "},
- {float32(-0.5), " -0.5 "},
- {float32(1.0) / float32(256), " 0.00390625 "},
- {float32(0), " 0 "},
- {math.Copysign(0, -1), " -0 "},
- {float64(1.0), " 1 "},
- {float64(-1.0), " -1 "},
- {float64(0.5), " 0.5 "},
- {float64(-0.5), " -0.5 "},
- {float64(0), " 0 "},
- {math.Copysign(0, -1), " -0 "},
- {"", `""`},
- {"foo", `"foo"`},
- // Newlines.
- {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
- // "\v" == "v" on IE 6 so use "\x0b" instead.
- {"\t\x0b", `"\u0009\u000b"`},
- {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
- {[]interface{}{}, "[]"},
- {[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
- {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
- {"<!--", `"\u003c!--"`},
- {"-->", `"--\u003e"`},
- {"<![CDATA[", `"\u003c![CDATA["`},
- {"]]>", `"]]\u003e"`},
- {"</script", `"\u003c/script"`},
- {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
- }
-
- for _, test := range tests {
- if js := jsValEscaper(test.x); js != test.js {
- t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
- }
- // Make sure that escaping corner cases are not broken
- // by nesting.
- a := []interface{}{test.x}
- want := "[" + strings.TrimSpace(test.js) + "]"
- if js := jsValEscaper(a); js != want {
- t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js)
- }
- }
-}
-
-func TestJSStrEscaper(t *testing.T) {
- tests := []struct {
- x interface{}
- esc string
- }{
- {"", ``},
- {"foo", `foo`},
- {"\u0000", `\0`},
- {"\t", `\t`},
- {"\n", `\n`},
- {"\r", `\r`},
- {"\u2028", `\u2028`},
- {"\u2029", `\u2029`},
- {"\\", `\\`},
- {"\\n", `\\n`},
- {"foo\r\nbar", `foo\r\nbar`},
- // Preserve attribute boundaries.
- {`"`, `\x22`},
- {`'`, `\x27`},
- // Allow embedding in HTML without further escaping.
- {`&amp;`, `\x26amp;`},
- // Prevent breaking out of text node and element boundaries.
- {"</script>", `\x3c\/script\x3e`},
- {"<![CDATA[", `\x3c![CDATA[`},
- {"]]>", `]]\x3e`},
- // http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
- // "The text in style, script, title, and textarea elements
- // must not have an escaping text span start that is not
- // followed by an escaping text span end."
- // Furthermore, spoofing an escaping text span end could lead
- // to different interpretation of a </script> sequence otherwise
- // masked by the escaping text span, and spoofing a start could
- // allow regular text content to be interpreted as script
- // allowing script execution via a combination of a JS string
- // injection followed by an HTML text injection.
- {"<!--", `\x3c!--`},
- {"-->", `--\x3e`},
- // From http://code.google.com/p/doctype/wiki/ArticleUtf7
- {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
- `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
- },
- // Invalid UTF-8 sequence
- {"foo\xA0bar", "foo\xA0bar"},
- // Invalid unicode scalar value.
- {"foo\xed\xa0\x80bar", "foo\xed\xa0\x80bar"},
- }
-
- for _, test := range tests {
- esc := jsStrEscaper(test.x)
- if esc != test.esc {
- t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
- }
- }
-}
-
-func TestJSRegexpEscaper(t *testing.T) {
- tests := []struct {
- x interface{}
- esc string
- }{
- {"", `(?:)`},
- {"foo", `foo`},
- {"\u0000", `\0`},
- {"\t", `\t`},
- {"\n", `\n`},
- {"\r", `\r`},
- {"\u2028", `\u2028`},
- {"\u2029", `\u2029`},
- {"\\", `\\`},
- {"\\n", `\\n`},
- {"foo\r\nbar", `foo\r\nbar`},
- // Preserve attribute boundaries.
- {`"`, `\x22`},
- {`'`, `\x27`},
- // Allow embedding in HTML without further escaping.
- {`&amp;`, `\x26amp;`},
- // Prevent breaking out of text node and element boundaries.
- {"</script>", `\x3c\/script\x3e`},
- {"<![CDATA[", `\x3c!\[CDATA\[`},
- {"]]>", `\]\]\x3e`},
- // Escaping text spans.
- {"<!--", `\x3c!\-\-`},
- {"-->", `\-\-\x3e`},
- {"*", `\*`},
- {"+", `\x2b`},
- {"?", `\?`},
- {"[](){}", `\[\]\(\)\{\}`},
- {"$foo|x.y", `\$foo\|x\.y`},
- {"x^y", `x\^y`},
- }
-
- for _, test := range tests {
- esc := jsRegexpEscaper(test.x)
- if esc != test.esc {
- t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
- }
- }
-}
-
-func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
- input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !"#$%&'()*+,-./` +
- `0123456789:;<=>?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
-
- tests := []struct {
- name string
- escaper func(...interface{}) string
- escaped string
- }{
- {
- "jsStrEscaper",
- jsStrEscaper,
- "\\0\x01\x02\x03\x04\x05\x06\x07" +
- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !\x22#$%\x26\x27()*\x2b,-.\/` +
- `0123456789:;\x3c=\x3e?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
- },
- {
- "jsRegexpEscaper",
- jsRegexpEscaper,
- "\\0\x01\x02\x03\x04\x05\x06\x07" +
- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
- `0123456789:;\x3c=\x3e\?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ\[\\\]\^_` +
- "`abcdefghijklmno" +
- `pqrstuvwxyz\{\|\}~` + "\u007f" +
- "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
- },
- }
-
- for _, test := range tests {
- if s := test.escaper(input); s != test.escaped {
- t.Errorf("%s once: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
- continue
- }
-
- // Escape it rune by rune to make sure that any
- // fast-path checking does not break escaping.
- var buf bytes.Buffer
- for _, c := range input {
- buf.WriteString(test.escaper(string(c)))
- }
-
- if s := buf.String(); s != test.escaped {
- t.Errorf("%s rune-wise: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
- continue
- }
- }
-}
-
-func BenchmarkJSValEscaperWithNum(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsValEscaper(3.141592654)
- }
-}
-
-func BenchmarkJSValEscaperWithStr(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsValEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
-
-func BenchmarkJSValEscaperWithStrNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsValEscaper("The quick, brown fox jumps over the lazy dog")
- }
-}
-
-func BenchmarkJSValEscaperWithObj(b *testing.B) {
- o := struct {
- S string
- N int
- }{
- "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>\u2028",
- 42,
- }
- for i := 0; i < b.N; i++ {
- jsValEscaper(o)
- }
-}
-
-func BenchmarkJSValEscaperWithObjNoSpecials(b *testing.B) {
- o := struct {
- S string
- N int
- }{
- "The quick, brown fox jumps over the lazy dog",
- 42,
- }
- for i := 0; i < b.N; i++ {
- jsValEscaper(o)
- }
-}
-
-func BenchmarkJSStrEscaperNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsStrEscaper("The quick, brown fox jumps over the lazy dog.")
- }
-}
-
-func BenchmarkJSStrEscaper(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsStrEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
-
-func BenchmarkJSRegexpEscaperNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsRegexpEscaper("The quick, brown fox jumps over the lazy dog")
- }
-}
-
-func BenchmarkJSRegexpEscaper(b *testing.B) {
- for i := 0; i < b.N; i++ {
- jsRegexpEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
- }
-}
diff --git a/src/pkg/html/template/template.go b/src/pkg/html/template/template.go
deleted file mode 100644
index d38965897..000000000
--- a/src/pkg/html/template/template.go
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "path/filepath"
- "sync"
- "text/template"
- "text/template/parse"
-)
-
-// Template is a specialized Template from "text/template" that produces a safe
-// HTML document fragment.
-type Template struct {
- escaped bool
- // We could embed the text/template field, but it's safer not to because
- // we need to keep our version of the name space and the underlying
- // template's in sync.
- text *template.Template
- // The underlying template's parse tree, updated to be HTML-safe.
- Tree *parse.Tree
- *nameSpace // common to all associated templates
-}
-
-// nameSpace is the data structure shared by all templates in an association.
-type nameSpace struct {
- mu sync.Mutex
- set map[string]*Template
-}
-
-// Templates returns a slice of the templates associated with t, including t
-// itself.
-func (t *Template) Templates() []*Template {
- ns := t.nameSpace
- ns.mu.Lock()
- defer ns.mu.Unlock()
- // Return a slice so we don't expose the map.
- m := make([]*Template, 0, len(ns.set))
- for _, v := range ns.set {
- m = append(m, v)
- }
- return m
-}
-
-// escape escapes all associated templates.
-func (t *Template) escape() error {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- if !t.escaped {
- if err := escapeTemplates(t, t.Name()); err != nil {
- return err
- }
- t.escaped = true
- }
- return nil
-}
-
-// Execute applies a parsed template to the specified data object,
-// writing the output to wr.
-// If an error occurs executing the template or writing its output,
-// execution stops, but partial results may already have been written to
-// the output writer.
-// A template may be executed safely in parallel.
-func (t *Template) Execute(wr io.Writer, data interface{}) error {
- if err := t.escape(); err != nil {
- return err
- }
- return t.text.Execute(wr, data)
-}
-
-// ExecuteTemplate applies the template associated with t that has the given
-// name to the specified data object and writes the output to wr.
-// If an error occurs executing the template or writing its output,
-// execution stops, but partial results may already have been written to
-// the output writer.
-// A template may be executed safely in parallel.
-func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
- tmpl, err := t.lookupAndEscapeTemplate(name)
- if err != nil {
- return err
- }
- return tmpl.text.Execute(wr, data)
-}
-
-// lookupAndEscapeTemplate guarantees that the template with the given name
-// is escaped, or returns an error if it cannot be. It returns the named
-// template.
-func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- tmpl = t.set[name]
- if tmpl == nil {
- return nil, fmt.Errorf("html/template: %q is undefined", name)
- }
- if tmpl.text.Tree == nil || tmpl.text.Root == nil {
- return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
- }
- if t.text.Lookup(name) == nil {
- panic("html/template internal error: template escaping out of sync")
- }
- if tmpl != nil && !tmpl.escaped {
- err = escapeTemplates(tmpl, name)
- }
- return tmpl, err
-}
-
-// Parse parses a string into a template. Nested template definitions
-// will be associated with the top-level template t. Parse may be
-// called multiple times to parse definitions of templates to associate
-// with t. It is an error if a resulting template is non-empty (contains
-// content other than template definitions) and would replace a
-// non-empty template with the same name. (In multiple calls to Parse
-// with the same receiver template, only one call can contain text
-// other than space, comments, and template definitions.)
-func (t *Template) Parse(src string) (*Template, error) {
- t.nameSpace.mu.Lock()
- t.escaped = false
- t.nameSpace.mu.Unlock()
- ret, err := t.text.Parse(src)
- if err != nil {
- return nil, err
- }
- // In general, all the named templates might have changed underfoot.
- // Regardless, some new ones may have been defined.
- // The template.Template set has been updated; update ours.
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- for _, v := range ret.Templates() {
- name := v.Name()
- tmpl := t.set[name]
- if tmpl == nil {
- tmpl = t.new(name)
- }
- // Restore our record of this text/template to its unescaped original state.
- tmpl.escaped = false
- tmpl.text = v
- tmpl.Tree = v.Tree
- }
- return t, nil
-}
-
-// AddParseTree creates a new template with the name and parse tree
-// and associates it with t.
-//
-// It returns an error if t has already been executed.
-func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- if t.escaped {
- return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
- }
- text, err := t.text.AddParseTree(name, tree)
- if err != nil {
- return nil, err
- }
- ret := &Template{
- false,
- text,
- text.Tree,
- t.nameSpace,
- }
- t.set[name] = ret
- return ret, nil
-}
-
-// Clone returns a duplicate of the template, including all associated
-// templates. The actual representation is not copied, but the name space of
-// associated templates is, so further calls to Parse in the copy will add
-// templates to the copy but not to the original. Clone can be used to prepare
-// common templates and use them with variant definitions for other templates
-// by adding the variants after the clone is made.
-//
-// It returns an error if t has already been executed.
-func (t *Template) Clone() (*Template, error) {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- if t.escaped {
- return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
- }
- textClone, err := t.text.Clone()
- if err != nil {
- return nil, err
- }
- ret := &Template{
- false,
- textClone,
- textClone.Tree,
- &nameSpace{
- set: make(map[string]*Template),
- },
- }
- for _, x := range textClone.Templates() {
- name := x.Name()
- src := t.set[name]
- if src == nil || src.escaped {
- return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
- }
- x.Tree = x.Tree.Copy()
- ret.set[name] = &Template{
- false,
- x,
- x.Tree,
- ret.nameSpace,
- }
- }
- return ret, nil
-}
-
-// New allocates a new HTML template with the given name.
-func New(name string) *Template {
- tmpl := &Template{
- false,
- template.New(name),
- nil,
- &nameSpace{
- set: make(map[string]*Template),
- },
- }
- tmpl.set[name] = tmpl
- return tmpl
-}
-
-// New allocates a new HTML template associated with the given one
-// and with the same delimiters. The association, which is transitive,
-// allows one template to invoke another with a {{template}} action.
-func (t *Template) New(name string) *Template {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- return t.new(name)
-}
-
-// new is the implementation of New, without the lock.
-func (t *Template) new(name string) *Template {
- tmpl := &Template{
- false,
- t.text.New(name),
- nil,
- t.nameSpace,
- }
- tmpl.set[name] = tmpl
- return tmpl
-}
-
-// Name returns the name of the template.
-func (t *Template) Name() string {
- return t.text.Name()
-}
-
-// FuncMap is the type of the map defining the mapping from names to
-// functions. Each function must have either a single return value, or two
-// return values of which the second has type error. In that case, if the
-// second (error) argument evaluates to non-nil during execution, execution
-// terminates and Execute returns that error. FuncMap has the same base type
-// as FuncMap in "text/template", copied here so clients need not import
-// "text/template".
-type FuncMap map[string]interface{}
-
-// Funcs adds the elements of the argument map to the template's function map.
-// It panics if a value in the map is not a function with appropriate return
-// type. However, it is legal to overwrite elements of the map. The return
-// value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap FuncMap) *Template {
- t.text.Funcs(template.FuncMap(funcMap))
- return t
-}
-
-// Delims sets the action delimiters to the specified strings, to be used in
-// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
-// definitions will inherit the settings. An empty delimiter stands for the
-// corresponding default: {{ or }}.
-// The return value is the template, so calls can be chained.
-func (t *Template) Delims(left, right string) *Template {
- t.text.Delims(left, right)
- return t
-}
-
-// Lookup returns the template with the given name that is associated with t,
-// or nil if there is no such template.
-func (t *Template) Lookup(name string) *Template {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- return t.set[name]
-}
-
-// Must is a helper that wraps a call to a function returning (*Template, error)
-// and panics if the error is non-nil. It is intended for use in variable initializations
-// such as
-// var t = template.Must(template.New("name").Parse("html"))
-func Must(t *Template, err error) *Template {
- if err != nil {
- panic(err)
- }
- return t
-}
-
-// ParseFiles creates a new Template and parses the template definitions from
-// the named files. The returned template's name will have the (base) name and
-// (parsed) contents of the first file. There must be at least one file.
-// If an error occurs, parsing stops and the returned *Template is nil.
-func ParseFiles(filenames ...string) (*Template, error) {
- return parseFiles(nil, filenames...)
-}
-
-// ParseFiles parses the named files and associates the resulting templates with
-// t. If an error occurs, parsing stops and the returned template is nil;
-// otherwise it is t. There must be at least one file.
-func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
- return parseFiles(t, filenames...)
-}
-
-// parseFiles is the helper for the method and function. If the argument
-// template is nil, it is created from the first file.
-func parseFiles(t *Template, filenames ...string) (*Template, error) {
- if len(filenames) == 0 {
- // Not really a problem, but be consistent.
- return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
- }
- for _, filename := range filenames {
- b, err := ioutil.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- s := string(b)
- name := filepath.Base(filename)
- // First template becomes return value if not already defined,
- // and we use that one for subsequent New calls to associate
- // all the templates together. Also, if this file has the same name
- // as t, this file becomes the contents of t, so
- // t, err := New(name).Funcs(xxx).ParseFiles(name)
- // works. Otherwise we create a new template associated with t.
- var tmpl *Template
- if t == nil {
- t = New(name)
- }
- if name == t.Name() {
- tmpl = t
- } else {
- tmpl = t.New(name)
- }
- _, err = tmpl.Parse(s)
- if err != nil {
- return nil, err
- }
- }
- return t, nil
-}
-
-// ParseGlob creates a new Template and parses the template definitions from the
-// files identified by the pattern, which must match at least one file. The
-// returned template will have the (base) name and (parsed) contents of the
-// first file matched by the pattern. ParseGlob is equivalent to calling
-// ParseFiles with the list of files matched by the pattern.
-func ParseGlob(pattern string) (*Template, error) {
- return parseGlob(nil, pattern)
-}
-
-// ParseGlob parses the template definitions in the files identified by the
-// pattern and associates the resulting templates with t. The pattern is
-// processed by filepath.Glob and must match at least one file. ParseGlob is
-// equivalent to calling t.ParseFiles with the list of files matched by the
-// pattern.
-func (t *Template) ParseGlob(pattern string) (*Template, error) {
- return parseGlob(t, pattern)
-}
-
-// parseGlob is the implementation of the function and method ParseGlob.
-func parseGlob(t *Template, pattern string) (*Template, error) {
- filenames, err := filepath.Glob(pattern)
- if err != nil {
- return nil, err
- }
- if len(filenames) == 0 {
- return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
- }
- return parseFiles(t, filenames...)
-}
diff --git a/src/pkg/html/template/transition.go b/src/pkg/html/template/transition.go
deleted file mode 100644
index 7f30a7ab8..000000000
--- a/src/pkg/html/template/transition.go
+++ /dev/null
@@ -1,550 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "strings"
-)
-
-// transitionFunc is the array of context transition functions for text nodes.
-// A transition function takes a context and template text input, and returns
-// the updated context and the number of bytes consumed from the front of the
-// input.
-var transitionFunc = [...]func(context, []byte) (context, int){
- stateText: tText,
- stateTag: tTag,
- stateAttrName: tAttrName,
- stateAfterName: tAfterName,
- stateBeforeValue: tBeforeValue,
- stateHTMLCmt: tHTMLCmt,
- stateRCDATA: tSpecialTagEnd,
- stateAttr: tAttr,
- stateURL: tURL,
- stateJS: tJS,
- stateJSDqStr: tJSDelimited,
- stateJSSqStr: tJSDelimited,
- stateJSRegexp: tJSDelimited,
- stateJSBlockCmt: tBlockCmt,
- stateJSLineCmt: tLineCmt,
- stateCSS: tCSS,
- stateCSSDqStr: tCSSStr,
- stateCSSSqStr: tCSSStr,
- stateCSSDqURL: tCSSStr,
- stateCSSSqURL: tCSSStr,
- stateCSSURL: tCSSStr,
- stateCSSBlockCmt: tBlockCmt,
- stateCSSLineCmt: tLineCmt,
- stateError: tError,
-}
-
-var commentStart = []byte("<!--")
-var commentEnd = []byte("-->")
-
-// tText is the context transition function for the text state.
-func tText(c context, s []byte) (context, int) {
- k := 0
- for {
- i := k + bytes.IndexByte(s[k:], '<')
- if i < k || i+1 == len(s) {
- return c, len(s)
- } else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) {
- return context{state: stateHTMLCmt}, i + 4
- }
- i++
- end := false
- if s[i] == '/' {
- if i+1 == len(s) {
- return c, len(s)
- }
- end, i = true, i+1
- }
- j, e := eatTagName(s, i)
- if j != i {
- if end {
- e = elementNone
- }
- // We've found an HTML tag.
- return context{state: stateTag, element: e}, j
- }
- k = j
- }
-}
-
-var elementContentType = [...]state{
- elementNone: stateText,
- elementScript: stateJS,
- elementStyle: stateCSS,
- elementTextarea: stateRCDATA,
- elementTitle: stateRCDATA,
-}
-
-// tTag is the context transition function for the tag state.
-func tTag(c context, s []byte) (context, int) {
- // Find the attribute name.
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- }
- if s[i] == '>' {
- return context{
- state: elementContentType[c.element],
- element: c.element,
- }, i + 1
- }
- j, err := eatAttrName(s, i)
- if err != nil {
- return context{state: stateError, err: err}, len(s)
- }
- state, attr := stateTag, attrNone
- if i == j {
- return context{
- state: stateError,
- err: errorf(ErrBadHTML, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
- }, len(s)
- }
- switch attrType(string(s[i:j])) {
- case contentTypeURL:
- attr = attrURL
- case contentTypeCSS:
- attr = attrStyle
- case contentTypeJS:
- attr = attrScript
- }
- if j == len(s) {
- state = stateAttrName
- } else {
- state = stateAfterName
- }
- return context{state: state, element: c.element, attr: attr}, j
-}
-
-// tAttrName is the context transition function for stateAttrName.
-func tAttrName(c context, s []byte) (context, int) {
- i, err := eatAttrName(s, 0)
- if err != nil {
- return context{state: stateError, err: err}, len(s)
- } else if i != len(s) {
- c.state = stateAfterName
- }
- return c, i
-}
-
-// tAfterName is the context transition function for stateAfterName.
-func tAfterName(c context, s []byte) (context, int) {
- // Look for the start of the value.
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- } else if s[i] != '=' {
- // Occurs due to tag ending '>', and valueless attribute.
- c.state = stateTag
- return c, i
- }
- c.state = stateBeforeValue
- // Consume the "=".
- return c, i + 1
-}
-
-var attrStartStates = [...]state{
- attrNone: stateAttr,
- attrScript: stateJS,
- attrStyle: stateCSS,
- attrURL: stateURL,
-}
-
-// tBeforeValue is the context transition function for stateBeforeValue.
-func tBeforeValue(c context, s []byte) (context, int) {
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- }
- // Find the attribute delimiter.
- delim := delimSpaceOrTagEnd
- switch s[i] {
- case '\'':
- delim, i = delimSingleQuote, i+1
- case '"':
- delim, i = delimDoubleQuote, i+1
- }
- c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone
- return c, i
-}
-
-// tHTMLCmt is the context transition function for stateHTMLCmt.
-func tHTMLCmt(c context, s []byte) (context, int) {
- if i := bytes.Index(s, commentEnd); i != -1 {
- return context{}, i + 3
- }
- return c, len(s)
-}
-
-// specialTagEndMarkers maps element types to the character sequence that
-// case-insensitively signals the end of the special tag body.
-var specialTagEndMarkers = [...]string{
- elementScript: "</script",
- elementStyle: "</style",
- elementTextarea: "</textarea",
- elementTitle: "</title",
-}
-
-// tSpecialTagEnd is the context transition function for raw text and RCDATA
-// element states.
-func tSpecialTagEnd(c context, s []byte) (context, int) {
- if c.element != elementNone {
- if i := strings.Index(strings.ToLower(string(s)), specialTagEndMarkers[c.element]); i != -1 {
- return context{}, i
- }
- }
- return c, len(s)
-}
-
-// tAttr is the context transition function for the attribute state.
-func tAttr(c context, s []byte) (context, int) {
- return c, len(s)
-}
-
-// tURL is the context transition function for the URL state.
-func tURL(c context, s []byte) (context, int) {
- if bytes.IndexAny(s, "#?") >= 0 {
- c.urlPart = urlPartQueryOrFrag
- } else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone {
- // HTML5 uses "Valid URL potentially surrounded by spaces" for
- // attrs: http://www.w3.org/TR/html5/index.html#attributes-1
- c.urlPart = urlPartPreQuery
- }
- return c, len(s)
-}
-
-// tJS is the context transition function for the JS state.
-func tJS(c context, s []byte) (context, int) {
- i := bytes.IndexAny(s, `"'/`)
- if i == -1 {
- // Entire input is non string, comment, regexp tokens.
- c.jsCtx = nextJSCtx(s, c.jsCtx)
- return c, len(s)
- }
- c.jsCtx = nextJSCtx(s[:i], c.jsCtx)
- switch s[i] {
- case '"':
- c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
- case '\'':
- c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
- case '/':
- switch {
- case i+1 < len(s) && s[i+1] == '/':
- c.state, i = stateJSLineCmt, i+1
- case i+1 < len(s) && s[i+1] == '*':
- c.state, i = stateJSBlockCmt, i+1
- case c.jsCtx == jsCtxRegexp:
- c.state = stateJSRegexp
- case c.jsCtx == jsCtxDivOp:
- c.jsCtx = jsCtxRegexp
- default:
- return context{
- state: stateError,
- err: errorf(ErrSlashAmbig, 0, "'/' could start a division or regexp: %.32q", s[i:]),
- }, len(s)
- }
- default:
- panic("unreachable")
- }
- return c, i + 1
-}
-
-// tJSDelimited is the context transition function for the JS string and regexp
-// states.
-func tJSDelimited(c context, s []byte) (context, int) {
- specials := `\"`
- switch c.state {
- case stateJSSqStr:
- specials = `\'`
- case stateJSRegexp:
- specials = `\/[]`
- }
-
- k, inCharset := 0, false
- for {
- i := k + bytes.IndexAny(s[k:], specials)
- if i < k {
- break
- }
- switch s[i] {
- case '\\':
- i++
- if i == len(s) {
- return context{
- state: stateError,
- err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s),
- }, len(s)
- }
- case '[':
- inCharset = true
- case ']':
- inCharset = false
- default:
- // end delimiter
- if !inCharset {
- c.state, c.jsCtx = stateJS, jsCtxDivOp
- return c, i + 1
- }
- }
- k = i + 1
- }
-
- if inCharset {
- // This can be fixed by making context richer if interpolation
- // into charsets is desired.
- return context{
- state: stateError,
- err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s),
- }, len(s)
- }
-
- return c, len(s)
-}
-
-var blockCommentEnd = []byte("*/")
-
-// tBlockCmt is the context transition function for /*comment*/ states.
-func tBlockCmt(c context, s []byte) (context, int) {
- i := bytes.Index(s, blockCommentEnd)
- if i == -1 {
- return c, len(s)
- }
- switch c.state {
- case stateJSBlockCmt:
- c.state = stateJS
- case stateCSSBlockCmt:
- c.state = stateCSS
- default:
- panic(c.state.String())
- }
- return c, i + 2
-}
-
-// tLineCmt is the context transition function for //comment states.
-func tLineCmt(c context, s []byte) (context, int) {
- var lineTerminators string
- var endState state
- switch c.state {
- case stateJSLineCmt:
- lineTerminators, endState = "\n\r\u2028\u2029", stateJS
- case stateCSSLineCmt:
- lineTerminators, endState = "\n\f\r", stateCSS
- // Line comments are not part of any published CSS standard but
- // are supported by the 4 major browsers.
- // This defines line comments as
- // LINECOMMENT ::= "//" [^\n\f\d]*
- // since http://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines
- // newlines:
- // nl ::= #xA | #xD #xA | #xD | #xC
- default:
- panic(c.state.String())
- }
-
- i := bytes.IndexAny(s, lineTerminators)
- if i == -1 {
- return c, len(s)
- }
- c.state = endState
- // Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4
- // "However, the LineTerminator at the end of the line is not
- // considered to be part of the single-line comment; it is
- // recognized separately by the lexical grammar and becomes part
- // of the stream of input elements for the syntactic grammar."
- return c, i
-}
-
-// tCSS is the context transition function for the CSS state.
-func tCSS(c context, s []byte) (context, int) {
- // CSS quoted strings are almost never used except for:
- // (1) URLs as in background: "/foo.png"
- // (2) Multiword font-names as in font-family: "Times New Roman"
- // (3) List separators in content values as in inline-lists:
- // <style>
- // ul.inlineList { list-style: none; padding:0 }
- // ul.inlineList > li { display: inline }
- // ul.inlineList > li:before { content: ", " }
- // ul.inlineList > li:first-child:before { content: "" }
- // </style>
- // <ul class=inlineList><li>One<li>Two<li>Three</ul>
- // (4) Attribute value selectors as in a[href="http://example.com/"]
- //
- // We conservatively treat all strings as URLs, but make some
- // allowances to avoid confusion.
- //
- // In (1), our conservative assumption is justified.
- // In (2), valid font names do not contain ':', '?', or '#', so our
- // conservative assumption is fine since we will never transition past
- // urlPartPreQuery.
- // In (3), our protocol heuristic should not be tripped, and there
- // should not be non-space content after a '?' or '#', so as long as
- // we only %-encode RFC 3986 reserved characters we are ok.
- // In (4), we should URL escape for URL attributes, and for others we
- // have the attribute name available if our conservative assumption
- // proves problematic for real code.
-
- k := 0
- for {
- i := k + bytes.IndexAny(s[k:], `("'/`)
- if i < k {
- return c, len(s)
- }
- switch s[i] {
- case '(':
- // Look for url to the left.
- p := bytes.TrimRight(s[:i], "\t\n\f\r ")
- if endsWithCSSKeyword(p, "url") {
- j := len(s) - len(bytes.TrimLeft(s[i+1:], "\t\n\f\r "))
- switch {
- case j != len(s) && s[j] == '"':
- c.state, j = stateCSSDqURL, j+1
- case j != len(s) && s[j] == '\'':
- c.state, j = stateCSSSqURL, j+1
- default:
- c.state = stateCSSURL
- }
- return c, j
- }
- case '/':
- if i+1 < len(s) {
- switch s[i+1] {
- case '/':
- c.state = stateCSSLineCmt
- return c, i + 2
- case '*':
- c.state = stateCSSBlockCmt
- return c, i + 2
- }
- }
- case '"':
- c.state = stateCSSDqStr
- return c, i + 1
- case '\'':
- c.state = stateCSSSqStr
- return c, i + 1
- }
- k = i + 1
- }
-}
-
-// tCSSStr is the context transition function for the CSS string and URL states.
-func tCSSStr(c context, s []byte) (context, int) {
- var endAndEsc string
- switch c.state {
- case stateCSSDqStr, stateCSSDqURL:
- endAndEsc = `\"`
- case stateCSSSqStr, stateCSSSqURL:
- endAndEsc = `\'`
- case stateCSSURL:
- // Unquoted URLs end with a newline or close parenthesis.
- // The below includes the wc (whitespace character) and nl.
- endAndEsc = "\\\t\n\f\r )"
- default:
- panic(c.state.String())
- }
-
- k := 0
- for {
- i := k + bytes.IndexAny(s[k:], endAndEsc)
- if i < k {
- c, nread := tURL(c, decodeCSS(s[k:]))
- return c, k + nread
- }
- if s[i] == '\\' {
- i++
- if i == len(s) {
- return context{
- state: stateError,
- err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s),
- }, len(s)
- }
- } else {
- c.state = stateCSS
- return c, i + 1
- }
- c, _ = tURL(c, decodeCSS(s[:i+1]))
- k = i + 1
- }
-}
-
-// tError is the context transition function for the error state.
-func tError(c context, s []byte) (context, int) {
- return c, len(s)
-}
-
-// eatAttrName returns the largest j such that s[i:j] is an attribute name.
-// It returns an error if s[i:] does not look like it begins with an
-// attribute name, such as encountering a quote mark without a preceding
-// equals sign.
-func eatAttrName(s []byte, i int) (int, *Error) {
- for j := i; j < len(s); j++ {
- switch s[j] {
- case ' ', '\t', '\n', '\f', '\r', '=', '>':
- return j, nil
- case '\'', '"', '<':
- // These result in a parse warning in HTML5 and are
- // indicative of serious problems if seen in an attr
- // name in a template.
- return -1, errorf(ErrBadHTML, 0, "%q in attribute name: %.32q", s[j:j+1], s)
- default:
- // No-op.
- }
- }
- return len(s), nil
-}
-
-var elementNameMap = map[string]element{
- "script": elementScript,
- "style": elementStyle,
- "textarea": elementTextarea,
- "title": elementTitle,
-}
-
-// asciiAlpha reports whether c is an ASCII letter.
-func asciiAlpha(c byte) bool {
- return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
-}
-
-// asciiAlphaNum reports whether c is an ASCII letter or digit.
-func asciiAlphaNum(c byte) bool {
- return asciiAlpha(c) || '0' <= c && c <= '9'
-}
-
-// eatTagName returns the largest j such that s[i:j] is a tag name and the tag type.
-func eatTagName(s []byte, i int) (int, element) {
- if i == len(s) || !asciiAlpha(s[i]) {
- return i, elementNone
- }
- j := i + 1
- for j < len(s) {
- x := s[j]
- if asciiAlphaNum(x) {
- j++
- continue
- }
- // Allow "x-y" or "x:y" but not "x-", "-y", or "x--y".
- if (x == ':' || x == '-') && j+1 < len(s) && asciiAlphaNum(s[j+1]) {
- j += 2
- continue
- }
- break
- }
- return j, elementNameMap[strings.ToLower(string(s[i:j]))]
-}
-
-// eatWhiteSpace returns the largest j such that s[i:j] is white space.
-func eatWhiteSpace(s []byte, i int) int {
- for j := i; j < len(s); j++ {
- switch s[j] {
- case ' ', '\t', '\n', '\f', '\r':
- // No-op.
- default:
- return j
- }
- }
- return len(s)
-}
diff --git a/src/pkg/html/template/url.go b/src/pkg/html/template/url.go
deleted file mode 100644
index 2ca76bf38..000000000
--- a/src/pkg/html/template/url.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "strings"
-)
-
-// urlFilter returns its input unless it contains an unsafe protocol in which
-// case it defangs the entire URL.
-func urlFilter(args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeURL {
- return s
- }
- if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
- protocol := strings.ToLower(s[:i])
- if protocol != "http" && protocol != "https" && protocol != "mailto" {
- return "#" + filterFailsafe
- }
- }
- return s
-}
-
-// urlEscaper produces an output that can be embedded in a URL query.
-// The output can be embedded in an HTML attribute without further escaping.
-func urlEscaper(args ...interface{}) string {
- return urlProcessor(false, args...)
-}
-
-// urlEscaper normalizes URL content so it can be embedded in a quote-delimited
-// string or parenthesis delimited url(...).
-// The normalizer does not encode all HTML specials. Specifically, it does not
-// encode '&' so correct embedding in an HTML attribute requires escaping of
-// '&' to '&amp;'.
-func urlNormalizer(args ...interface{}) string {
- return urlProcessor(true, args...)
-}
-
-// urlProcessor normalizes (when norm is true) or escapes its input to produce
-// a valid hierarchical or opaque URL part.
-func urlProcessor(norm bool, args ...interface{}) string {
- s, t := stringify(args...)
- if t == contentTypeURL {
- norm = true
- }
- var b bytes.Buffer
- written := 0
- // The byte loop below assumes that all URLs use UTF-8 as the
- // content-encoding. This is similar to the URI to IRI encoding scheme
- // defined in section 3.1 of RFC 3987, and behaves the same as the
- // EcmaScript builtin encodeURIComponent.
- // It should not cause any misencoding of URLs in pages with
- // Content-type: text/html;charset=UTF-8.
- for i, n := 0, len(s); i < n; i++ {
- c := s[i]
- switch c {
- // Single quote and parens are sub-delims in RFC 3986, but we
- // escape them so the output can be embedded in single
- // quoted attributes and unquoted CSS url(...) constructs.
- // Single quotes are reserved in URLs, but are only used in
- // the obsolete "mark" rule in an appendix in RFC 3986
- // so can be safely encoded.
- case '!', '#', '$', '&', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']':
- if norm {
- continue
- }
- // Unreserved according to RFC 3986 sec 2.3
- // "For consistency, percent-encoded octets in the ranges of
- // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
- // period (%2E), underscore (%5F), or tilde (%7E) should not be
- // created by URI producers
- case '-', '.', '_', '~':
- continue
- case '%':
- // When normalizing do not re-encode valid escapes.
- if norm && i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) {
- continue
- }
- default:
- // Unreserved according to RFC 3986 sec 2.3
- if 'a' <= c && c <= 'z' {
- continue
- }
- if 'A' <= c && c <= 'Z' {
- continue
- }
- if '0' <= c && c <= '9' {
- continue
- }
- }
- b.WriteString(s[written:i])
- fmt.Fprintf(&b, "%%%02x", c)
- written = i + 1
- }
- if written == 0 {
- return s
- }
- b.WriteString(s[written:])
- return b.String()
-}
diff --git a/src/pkg/html/template/url_test.go b/src/pkg/html/template/url_test.go
deleted file mode 100644
index 5182e9d79..000000000
--- a/src/pkg/html/template/url_test.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "testing"
-)
-
-func TestURLNormalizer(t *testing.T) {
- tests := []struct {
- url, want string
- }{
- {"", ""},
- {
- "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag",
- "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag",
- },
- {" ", "%20"},
- {"%7c", "%7c"},
- {"%7C", "%7C"},
- {"%2", "%252"},
- {"%", "%25"},
- {"%z", "%25z"},
- {"/foo|bar/%5c\u1234", "/foo%7cbar/%5c%e1%88%b4"},
- }
- for _, test := range tests {
- if got := urlNormalizer(test.url); test.want != got {
- t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.url, test.want, got)
- }
- if test.want != urlNormalizer(test.want) {
- t.Errorf("not idempotent: %q", test.want)
- }
- }
-}
-
-func TestURLFilters(t *testing.T) {
- input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
- ` !"#$%&'()*+,-./` +
- `0123456789:;<=>?` +
- `@ABCDEFGHIJKLMNO` +
- `PQRSTUVWXYZ[\]^_` +
- "`abcdefghijklmno" +
- "pqrstuvwxyz{|}~\x7f" +
- "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
-
- tests := []struct {
- name string
- escaper func(...interface{}) string
- escaped string
- }{
- {
- "urlEscaper",
- urlEscaper,
- "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" +
- "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" +
- "%20%21%22%23%24%25%26%27%28%29%2a%2b%2c-.%2f" +
- "0123456789%3a%3b%3c%3d%3e%3f" +
- "%40ABCDEFGHIJKLMNO" +
- "PQRSTUVWXYZ%5b%5c%5d%5e_" +
- "%60abcdefghijklmno" +
- "pqrstuvwxyz%7b%7c%7d~%7f" +
- "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e",
- },
- {
- "urlNormalizer",
- urlNormalizer,
- "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" +
- "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" +
- "%20!%22#$%25&%27%28%29*+,-./" +
- "0123456789:;%3c=%3e?" +
- "@ABCDEFGHIJKLMNO" +
- "PQRSTUVWXYZ[%5c]%5e_" +
- "%60abcdefghijklmno" +
- "pqrstuvwxyz%7b%7c%7d~%7f" +
- "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e",
- },
- }
-
- for _, test := range tests {
- if s := test.escaper(input); s != test.escaped {
- t.Errorf("%s: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
- continue
- }
- }
-}
-
-func BenchmarkURLEscaper(b *testing.B) {
- for i := 0; i < b.N; i++ {
- urlEscaper("http://example.com:80/foo?q=bar%20&baz=x+y#frag")
- }
-}
-
-func BenchmarkURLEscaperNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- urlEscaper("TheQuickBrownFoxJumpsOverTheLazyDog.")
- }
-}
-
-func BenchmarkURLNormalizer(b *testing.B) {
- for i := 0; i < b.N; i++ {
- urlNormalizer("The quick brown fox jumps over the lazy dog.\n")
- }
-}
-
-func BenchmarkURLNormalizerNoSpecials(b *testing.B) {
- for i := 0; i < b.N; i++ {
- urlNormalizer("http://example.com:80/foo?q=bar%20&baz=x+y#frag")
- }
-}