foreign_content.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. 'use strict';
  2. var Tokenizer = require('../tokenizer'),
  3. HTML = require('./html');
  4. //Aliases
  5. var $ = HTML.TAG_NAMES,
  6. NS = HTML.NAMESPACES,
  7. ATTRS = HTML.ATTRS;
  8. //MIME types
  9. var MIME_TYPES = {
  10. TEXT_HTML: 'text/html',
  11. APPLICATION_XML: 'application/xhtml+xml'
  12. };
  13. //Attributes
  14. var DEFINITION_URL_ATTR = 'definitionurl',
  15. ADJUSTED_DEFINITION_URL_ATTR = 'definitionURL',
  16. SVG_ATTRS_ADJUSTMENT_MAP = {
  17. 'attributename': 'attributeName',
  18. 'attributetype': 'attributeType',
  19. 'basefrequency': 'baseFrequency',
  20. 'baseprofile': 'baseProfile',
  21. 'calcmode': 'calcMode',
  22. 'clippathunits': 'clipPathUnits',
  23. 'diffuseconstant': 'diffuseConstant',
  24. 'edgemode': 'edgeMode',
  25. 'filterunits': 'filterUnits',
  26. 'glyphref': 'glyphRef',
  27. 'gradienttransform': 'gradientTransform',
  28. 'gradientunits': 'gradientUnits',
  29. 'kernelmatrix': 'kernelMatrix',
  30. 'kernelunitlength': 'kernelUnitLength',
  31. 'keypoints': 'keyPoints',
  32. 'keysplines': 'keySplines',
  33. 'keytimes': 'keyTimes',
  34. 'lengthadjust': 'lengthAdjust',
  35. 'limitingconeangle': 'limitingConeAngle',
  36. 'markerheight': 'markerHeight',
  37. 'markerunits': 'markerUnits',
  38. 'markerwidth': 'markerWidth',
  39. 'maskcontentunits': 'maskContentUnits',
  40. 'maskunits': 'maskUnits',
  41. 'numoctaves': 'numOctaves',
  42. 'pathlength': 'pathLength',
  43. 'patterncontentunits': 'patternContentUnits',
  44. 'patterntransform': 'patternTransform',
  45. 'patternunits': 'patternUnits',
  46. 'pointsatx': 'pointsAtX',
  47. 'pointsaty': 'pointsAtY',
  48. 'pointsatz': 'pointsAtZ',
  49. 'preservealpha': 'preserveAlpha',
  50. 'preserveaspectratio': 'preserveAspectRatio',
  51. 'primitiveunits': 'primitiveUnits',
  52. 'refx': 'refX',
  53. 'refy': 'refY',
  54. 'repeatcount': 'repeatCount',
  55. 'repeatdur': 'repeatDur',
  56. 'requiredextensions': 'requiredExtensions',
  57. 'requiredfeatures': 'requiredFeatures',
  58. 'specularconstant': 'specularConstant',
  59. 'specularexponent': 'specularExponent',
  60. 'spreadmethod': 'spreadMethod',
  61. 'startoffset': 'startOffset',
  62. 'stddeviation': 'stdDeviation',
  63. 'stitchtiles': 'stitchTiles',
  64. 'surfacescale': 'surfaceScale',
  65. 'systemlanguage': 'systemLanguage',
  66. 'tablevalues': 'tableValues',
  67. 'targetx': 'targetX',
  68. 'targety': 'targetY',
  69. 'textlength': 'textLength',
  70. 'viewbox': 'viewBox',
  71. 'viewtarget': 'viewTarget',
  72. 'xchannelselector': 'xChannelSelector',
  73. 'ychannelselector': 'yChannelSelector',
  74. 'zoomandpan': 'zoomAndPan'
  75. },
  76. XML_ATTRS_ADJUSTMENT_MAP = {
  77. 'xlink:actuate': {prefix: 'xlink', name: 'actuate', namespace: NS.XLINK},
  78. 'xlink:arcrole': {prefix: 'xlink', name: 'arcrole', namespace: NS.XLINK},
  79. 'xlink:href': {prefix: 'xlink', name: 'href', namespace: NS.XLINK},
  80. 'xlink:role': {prefix: 'xlink', name: 'role', namespace: NS.XLINK},
  81. 'xlink:show': {prefix: 'xlink', name: 'show', namespace: NS.XLINK},
  82. 'xlink:title': {prefix: 'xlink', name: 'title', namespace: NS.XLINK},
  83. 'xlink:type': {prefix: 'xlink', name: 'type', namespace: NS.XLINK},
  84. 'xml:base': {prefix: 'xml', name: 'base', namespace: NS.XML},
  85. 'xml:lang': {prefix: 'xml', name: 'lang', namespace: NS.XML},
  86. 'xml:space': {prefix: 'xml', name: 'space', namespace: NS.XML},
  87. 'xmlns': {prefix: '', name: 'xmlns', namespace: NS.XMLNS},
  88. 'xmlns:xlink': {prefix: 'xmlns', name: 'xlink', namespace: NS.XMLNS}
  89. };
  90. //SVG tag names adjustment map
  91. var SVG_TAG_NAMES_ADJUSTMENT_MAP = exports.SVG_TAG_NAMES_ADJUSTMENT_MAP = {
  92. 'altglyph': 'altGlyph',
  93. 'altglyphdef': 'altGlyphDef',
  94. 'altglyphitem': 'altGlyphItem',
  95. 'animatecolor': 'animateColor',
  96. 'animatemotion': 'animateMotion',
  97. 'animatetransform': 'animateTransform',
  98. 'clippath': 'clipPath',
  99. 'feblend': 'feBlend',
  100. 'fecolormatrix': 'feColorMatrix',
  101. 'fecomponenttransfer': 'feComponentTransfer',
  102. 'fecomposite': 'feComposite',
  103. 'feconvolvematrix': 'feConvolveMatrix',
  104. 'fediffuselighting': 'feDiffuseLighting',
  105. 'fedisplacementmap': 'feDisplacementMap',
  106. 'fedistantlight': 'feDistantLight',
  107. 'feflood': 'feFlood',
  108. 'fefunca': 'feFuncA',
  109. 'fefuncb': 'feFuncB',
  110. 'fefuncg': 'feFuncG',
  111. 'fefuncr': 'feFuncR',
  112. 'fegaussianblur': 'feGaussianBlur',
  113. 'feimage': 'feImage',
  114. 'femerge': 'feMerge',
  115. 'femergenode': 'feMergeNode',
  116. 'femorphology': 'feMorphology',
  117. 'feoffset': 'feOffset',
  118. 'fepointlight': 'fePointLight',
  119. 'fespecularlighting': 'feSpecularLighting',
  120. 'fespotlight': 'feSpotLight',
  121. 'fetile': 'feTile',
  122. 'feturbulence': 'feTurbulence',
  123. 'foreignobject': 'foreignObject',
  124. 'glyphref': 'glyphRef',
  125. 'lineargradient': 'linearGradient',
  126. 'radialgradient': 'radialGradient',
  127. 'textpath': 'textPath'
  128. };
  129. //Tags that causes exit from foreign content
  130. var EXITS_FOREIGN_CONTENT = Object.create(null);
  131. EXITS_FOREIGN_CONTENT[$.B] = true;
  132. EXITS_FOREIGN_CONTENT[$.BIG] = true;
  133. EXITS_FOREIGN_CONTENT[$.BLOCKQUOTE] = true;
  134. EXITS_FOREIGN_CONTENT[$.BODY] = true;
  135. EXITS_FOREIGN_CONTENT[$.BR] = true;
  136. EXITS_FOREIGN_CONTENT[$.CENTER] = true;
  137. EXITS_FOREIGN_CONTENT[$.CODE] = true;
  138. EXITS_FOREIGN_CONTENT[$.DD] = true;
  139. EXITS_FOREIGN_CONTENT[$.DIV] = true;
  140. EXITS_FOREIGN_CONTENT[$.DL] = true;
  141. EXITS_FOREIGN_CONTENT[$.DT] = true;
  142. EXITS_FOREIGN_CONTENT[$.EM] = true;
  143. EXITS_FOREIGN_CONTENT[$.EMBED] = true;
  144. EXITS_FOREIGN_CONTENT[$.H1] = true;
  145. EXITS_FOREIGN_CONTENT[$.H2] = true;
  146. EXITS_FOREIGN_CONTENT[$.H3] = true;
  147. EXITS_FOREIGN_CONTENT[$.H4] = true;
  148. EXITS_FOREIGN_CONTENT[$.H5] = true;
  149. EXITS_FOREIGN_CONTENT[$.H6] = true;
  150. EXITS_FOREIGN_CONTENT[$.HEAD] = true;
  151. EXITS_FOREIGN_CONTENT[$.HR] = true;
  152. EXITS_FOREIGN_CONTENT[$.I] = true;
  153. EXITS_FOREIGN_CONTENT[$.IMG] = true;
  154. EXITS_FOREIGN_CONTENT[$.LI] = true;
  155. EXITS_FOREIGN_CONTENT[$.LISTING] = true;
  156. EXITS_FOREIGN_CONTENT[$.MENU] = true;
  157. EXITS_FOREIGN_CONTENT[$.META] = true;
  158. EXITS_FOREIGN_CONTENT[$.NOBR] = true;
  159. EXITS_FOREIGN_CONTENT[$.OL] = true;
  160. EXITS_FOREIGN_CONTENT[$.P] = true;
  161. EXITS_FOREIGN_CONTENT[$.PRE] = true;
  162. EXITS_FOREIGN_CONTENT[$.RUBY] = true;
  163. EXITS_FOREIGN_CONTENT[$.S] = true;
  164. EXITS_FOREIGN_CONTENT[$.SMALL] = true;
  165. EXITS_FOREIGN_CONTENT[$.SPAN] = true;
  166. EXITS_FOREIGN_CONTENT[$.STRONG] = true;
  167. EXITS_FOREIGN_CONTENT[$.STRIKE] = true;
  168. EXITS_FOREIGN_CONTENT[$.SUB] = true;
  169. EXITS_FOREIGN_CONTENT[$.SUP] = true;
  170. EXITS_FOREIGN_CONTENT[$.TABLE] = true;
  171. EXITS_FOREIGN_CONTENT[$.TT] = true;
  172. EXITS_FOREIGN_CONTENT[$.U] = true;
  173. EXITS_FOREIGN_CONTENT[$.UL] = true;
  174. EXITS_FOREIGN_CONTENT[$.VAR] = true;
  175. //Check exit from foreign content
  176. exports.causesExit = function (startTagToken) {
  177. var tn = startTagToken.tagName;
  178. var isFontWithAttrs = tn === $.FONT && (Tokenizer.getTokenAttr(startTagToken, ATTRS.COLOR) !== null ||
  179. Tokenizer.getTokenAttr(startTagToken, ATTRS.SIZE) !== null ||
  180. Tokenizer.getTokenAttr(startTagToken, ATTRS.FACE) !== null);
  181. return isFontWithAttrs ? true : EXITS_FOREIGN_CONTENT[tn];
  182. };
  183. //Token adjustments
  184. exports.adjustTokenMathMLAttrs = function (token) {
  185. for (var i = 0; i < token.attrs.length; i++) {
  186. if (token.attrs[i].name === DEFINITION_URL_ATTR) {
  187. token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
  188. break;
  189. }
  190. }
  191. };
  192. exports.adjustTokenSVGAttrs = function (token) {
  193. for (var i = 0; i < token.attrs.length; i++) {
  194. var adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
  195. if (adjustedAttrName)
  196. token.attrs[i].name = adjustedAttrName;
  197. }
  198. };
  199. exports.adjustTokenXMLAttrs = function (token) {
  200. for (var i = 0; i < token.attrs.length; i++) {
  201. var adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
  202. if (adjustedAttrEntry) {
  203. token.attrs[i].prefix = adjustedAttrEntry.prefix;
  204. token.attrs[i].name = adjustedAttrEntry.name;
  205. token.attrs[i].namespace = adjustedAttrEntry.namespace;
  206. }
  207. }
  208. };
  209. exports.adjustTokenSVGTagName = function (token) {
  210. var adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP[token.tagName];
  211. if (adjustedTagName)
  212. token.tagName = adjustedTagName;
  213. };
  214. //Integration points
  215. function isMathMLTextIntegrationPoint(tn, ns) {
  216. return ns === NS.MATHML && (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS || tn === $.MTEXT);
  217. }
  218. function isHtmlIntegrationPoint(tn, ns, attrs) {
  219. if (ns === NS.MATHML && tn === $.ANNOTATION_XML) {
  220. for (var i = 0; i < attrs.length; i++) {
  221. if (attrs[i].name === ATTRS.ENCODING) {
  222. var value = attrs[i].value.toLowerCase();
  223. return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML;
  224. }
  225. }
  226. }
  227. return ns === NS.SVG && (tn === $.FOREIGN_OBJECT || tn === $.DESC || tn === $.TITLE);
  228. }
  229. exports.isIntegrationPoint = function (tn, ns, attrs, foreignNS) {
  230. if ((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs))
  231. return true;
  232. if ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns))
  233. return true;
  234. return false;
  235. };