1/* 2 * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved. 3 * Copyright (C) 2010 Apple Inc. All rights reserved. 4 * Copyright (C) 2010 François Sausset (sausset@gmail.com). All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "config.h" 29 30#if ENABLE(MATHML) 31 32#include "MathMLElement.h" 33 34#include "ElementIterator.h" 35#include "HTMLElement.h" 36#include "HTMLMapElement.h" 37#include "HTMLNames.h" 38#include "MathMLNames.h" 39#include "MathMLSelectElement.h" 40#include "RenderTableCell.h" 41#include "SVGElement.h" 42#include "SVGNames.h" 43 44namespace WebCore { 45 46using namespace MathMLNames; 47 48MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document) 49 : StyledElement(tagName, document, CreateMathMLElement) 50{ 51} 52 53PassRefPtr<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document) 54{ 55 return adoptRef(new MathMLElement(tagName, document)); 56} 57 58bool MathMLElement::isPresentationMathML() const 59{ 60 return hasTagName(MathMLNames::mtrTag) 61 || hasTagName(MathMLNames::mtdTag) 62 || hasTagName(MathMLNames::maligngroupTag) 63 || hasTagName(MathMLNames::malignmarkTag) 64 || hasTagName(MathMLNames::mencloseTag) 65 || hasTagName(MathMLNames::mglyphTag) 66 || hasTagName(MathMLNames::mlabeledtrTag) 67 || hasTagName(MathMLNames::mlongdivTag) 68 || hasTagName(MathMLNames::mpaddedTag) 69 || hasTagName(MathMLNames::msTag) 70 || hasTagName(MathMLNames::mscarriesTag) 71 || hasTagName(MathMLNames::mscarryTag) 72 || hasTagName(MathMLNames::msgroupTag) 73 || hasTagName(MathMLNames::mslineTag) 74 || hasTagName(MathMLNames::msrowTag) 75 || hasTagName(MathMLNames::mstackTag); 76} 77 78bool MathMLElement::isPhrasingContent(const Node& node) const 79{ 80 // Phrasing content is described in the HTML 5 specification: 81 // http://www.w3.org/TR/html5/dom.html#phrasing-content. 82 83 if (!node.isElementNode()) 84 return node.isTextNode(); 85 86 if (node.isMathMLElement()) { 87 auto& mathmlElement = toMathMLElement(node); 88 return mathmlElement.hasTagName(MathMLNames::mathTag); 89 } 90 91 if (node.isSVGElement()) { 92 auto& svgElement = toSVGElement(node); 93 return svgElement.hasTagName(SVGNames::svgTag); 94 } 95 96 if (node.isHTMLElement()) { 97 // FIXME: add the <data> and <time> tags when they are implemented. 98 auto& htmlElement = toHTMLElement(node); 99 return htmlElement.hasTagName(HTMLNames::aTag) 100 || htmlElement.hasTagName(HTMLNames::abbrTag) 101 || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first()) 102 || htmlElement.hasTagName(HTMLNames::audioTag) 103 || htmlElement.hasTagName(HTMLNames::bTag) 104 || htmlElement.hasTagName(HTMLNames::bdiTag) 105 || htmlElement.hasTagName(HTMLNames::bdoTag) 106 || htmlElement.hasTagName(HTMLNames::brTag) 107 || htmlElement.hasTagName(HTMLNames::buttonTag) 108 || htmlElement.hasTagName(HTMLNames::canvasTag) 109 || htmlElement.hasTagName(HTMLNames::citeTag) 110 || htmlElement.hasTagName(HTMLNames::codeTag) 111 || htmlElement.hasTagName(HTMLNames::datalistTag) 112 || htmlElement.hasTagName(HTMLNames::delTag) 113 || htmlElement.hasTagName(HTMLNames::dfnTag) 114 || htmlElement.hasTagName(HTMLNames::emTag) 115 || htmlElement.hasTagName(HTMLNames::embedTag) 116 || htmlElement.hasTagName(HTMLNames::iTag) 117 || htmlElement.hasTagName(HTMLNames::iframeTag) 118 || htmlElement.hasTagName(HTMLNames::imgTag) 119 || htmlElement.hasTagName(HTMLNames::inputTag) 120 || htmlElement.hasTagName(HTMLNames::insTag) 121 || htmlElement.hasTagName(HTMLNames::kbdTag) 122 || htmlElement.hasTagName(HTMLNames::keygenTag) 123 || htmlElement.hasTagName(HTMLNames::labelTag) 124 || htmlElement.hasTagName(HTMLNames::mapTag) 125 || htmlElement.hasTagName(HTMLNames::markTag) 126 || htmlElement.hasTagName(HTMLNames::meterTag) 127 || htmlElement.hasTagName(HTMLNames::noscriptTag) 128 || htmlElement.hasTagName(HTMLNames::objectTag) 129 || htmlElement.hasTagName(HTMLNames::outputTag) 130 || htmlElement.hasTagName(HTMLNames::progressTag) 131 || htmlElement.hasTagName(HTMLNames::qTag) 132 || htmlElement.hasTagName(HTMLNames::rubyTag) 133 || htmlElement.hasTagName(HTMLNames::sTag) 134 || htmlElement.hasTagName(HTMLNames::sampTag) 135 || htmlElement.hasTagName(HTMLNames::scriptTag) 136 || htmlElement.hasTagName(HTMLNames::selectTag) 137 || htmlElement.hasTagName(HTMLNames::smallTag) 138 || htmlElement.hasTagName(HTMLNames::spanTag) 139 || htmlElement.hasTagName(HTMLNames::strongTag) 140 || htmlElement.hasTagName(HTMLNames::subTag) 141 || htmlElement.hasTagName(HTMLNames::supTag) 142 || htmlElement.hasTagName(HTMLNames::templateTag) 143 || htmlElement.hasTagName(HTMLNames::textareaTag) 144 || htmlElement.hasTagName(HTMLNames::uTag) 145 || htmlElement.hasTagName(HTMLNames::varTag) 146 || htmlElement.hasTagName(HTMLNames::videoTag) 147 || htmlElement.hasTagName(HTMLNames::wbrTag); 148 } 149 150 return false; 151} 152 153bool MathMLElement::isFlowContent(const Node& node) const 154{ 155 // Flow content is described in the HTML 5 specification: 156 // http://www.w3.org/TR/html5/dom.html#flow-content 157 158 if (isPhrasingContent(node)) 159 return true; 160 161 if (!node.isHTMLElement()) 162 return false; 163 164 auto& htmlElement = toHTMLElement(node); 165 // FIXME add the <dialog> tag when it is implemented. 166 return htmlElement.hasTagName(HTMLNames::addressTag) 167 || htmlElement.hasTagName(HTMLNames::articleTag) 168 || htmlElement.hasTagName(HTMLNames::asideTag) 169 || htmlElement.hasTagName(HTMLNames::blockquoteTag) 170 || htmlElement.hasTagName(HTMLNames::detailsTag) 171 || htmlElement.hasTagName(HTMLNames::divTag) 172 || htmlElement.hasTagName(HTMLNames::dlTag) 173 || htmlElement.hasTagName(HTMLNames::fieldsetTag) 174 || htmlElement.hasTagName(HTMLNames::figureTag) 175 || htmlElement.hasTagName(HTMLNames::footerTag) 176 || htmlElement.hasTagName(HTMLNames::formTag) 177 || htmlElement.hasTagName(HTMLNames::h1Tag) 178 || htmlElement.hasTagName(HTMLNames::h2Tag) 179 || htmlElement.hasTagName(HTMLNames::h3Tag) 180 || htmlElement.hasTagName(HTMLNames::h4Tag) 181 || htmlElement.hasTagName(HTMLNames::h5Tag) 182 || htmlElement.hasTagName(HTMLNames::h6Tag) 183 || htmlElement.hasTagName(HTMLNames::headerTag) 184 || htmlElement.hasTagName(HTMLNames::hrTag) 185 || htmlElement.hasTagName(HTMLNames::mainTag) 186 || htmlElement.hasTagName(HTMLNames::navTag) 187 || htmlElement.hasTagName(HTMLNames::olTag) 188 || htmlElement.hasTagName(HTMLNames::pTag) 189 || htmlElement.hasTagName(HTMLNames::preTag) 190 || htmlElement.hasTagName(HTMLNames::sectionTag) 191 || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped")) 192 || htmlElement.hasTagName(HTMLNames::tableTag) 193 || htmlElement.hasTagName(HTMLNames::ulTag); 194} 195 196int MathMLElement::colSpan() const 197{ 198 if (!hasTagName(mtdTag)) 199 return 1; 200 const AtomicString& colSpanValue = fastGetAttribute(columnspanAttr); 201 return std::max(1, colSpanValue.toInt()); 202} 203 204int MathMLElement::rowSpan() const 205{ 206 if (!hasTagName(mtdTag)) 207 return 1; 208 const AtomicString& rowSpanValue = fastGetAttribute(rowspanAttr); 209 return std::max(1, rowSpanValue.toInt()); 210} 211 212void MathMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 213{ 214 if (name == rowspanAttr) { 215 if (renderer() && renderer()->isTableCell() && hasTagName(mtdTag)) 216 toRenderTableCell(renderer())->colSpanOrRowSpanChanged(); 217 } else if (name == columnspanAttr) { 218 if (renderer() && renderer()->isTableCell() && hasTagName(mtdTag)) 219 toRenderTableCell(renderer())->colSpanOrRowSpanChanged(); 220 } else 221 StyledElement::parseAttribute(name, value); 222} 223 224bool MathMLElement::isPresentationAttribute(const QualifiedName& name) const 225{ 226 if (name == backgroundAttr || name == colorAttr || name == dirAttr || name == fontfamilyAttr || name == fontsizeAttr || name == fontstyleAttr || name == fontweightAttr || name == mathbackgroundAttr || name == mathcolorAttr || name == mathsizeAttr) 227 return true; 228 return StyledElement::isPresentationAttribute(name); 229} 230 231void MathMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style) 232{ 233 if (name == mathbackgroundAttr) 234 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value); 235 else if (name == mathsizeAttr) { 236 // The following three values of mathsize are handled in WebCore/css/mathml.css 237 if (value != "normal" && value != "small" && value != "big") 238 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value); 239 } else if (name == mathcolorAttr) 240 addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value); 241 // FIXME: deprecated attributes that should loose in a conflict with a non deprecated attribute 242 else if (name == fontsizeAttr) 243 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value); 244 else if (name == backgroundAttr) 245 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value); 246 else if (name == colorAttr) 247 addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value); 248 else if (name == fontstyleAttr) 249 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontStyle, value); 250 else if (name == fontweightAttr) 251 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontWeight, value); 252 else if (name == fontfamilyAttr) 253 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontFamily, value); 254 else if (name == dirAttr) { 255 if (hasTagName(mathTag) || hasTagName(mrowTag) || hasTagName(mstyleTag) || isMathMLToken()) 256 addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value); 257 } else { 258 ASSERT(!isPresentationAttribute(name)); 259 StyledElement::collectStyleForPresentationAttribute(name, value 260 , style); 261 } 262} 263 264bool MathMLElement::childShouldCreateRenderer(const Node& child) const 265{ 266 if (hasTagName(annotation_xmlTag)) { 267 const AtomicString& value = fastGetAttribute(MathMLNames::encodingAttr); 268 269 // See annotation-xml.model.mathml, annotation-xml.model.svg and annotation-xml.model.xhtml in the HTML5 RelaxNG schema. 270 271 if (child.isMathMLElement() && (MathMLSelectElement::isMathMLEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) { 272 auto& mathmlElement = toMathMLElement(child); 273 return mathmlElement.hasTagName(MathMLNames::mathTag); 274 } 275 276 if (child.isSVGElement() && (MathMLSelectElement::isSVGEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) { 277 auto& svgElement = toSVGElement(child); 278 return svgElement.hasTagName(SVGNames::svgTag); 279 } 280 281 if (child.isHTMLElement() && MathMLSelectElement::isHTMLEncoding(value)) { 282 auto& htmlElement = toHTMLElement(child); 283 return htmlElement.hasTagName(HTMLNames::htmlTag) || (isFlowContent(htmlElement) && StyledElement::childShouldCreateRenderer(child)); 284 } 285 286 return false; 287 } 288 289 // In general, only MathML children are allowed. Text nodes are only visible in token MathML elements. 290 return child.isMathMLElement(); 291} 292 293void MathMLElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason) 294{ 295 if (isSemanticAnnotation() && (name == MathMLNames::srcAttr || name == MathMLNames::encodingAttr)) { 296 Element* parent = parentElement(); 297 if (parent && parent->isMathMLElement() && parent->hasTagName(semanticsTag)) 298 toMathMLElement(parent)->updateSelectedChild(); 299 } 300 StyledElement::attributeChanged(name, oldValue, newValue, reason); 301} 302 303} 304 305#endif // ENABLE(MATHML) 306