1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #include "config.h" 25 #include "HTMLElement.h" 26 27 #include "CSSPropertyNames.h" 28 #include "CSSValueKeywords.h" 29 #include "DocumentFragment.h" 30 #include "Event.h" 31 #include "EventListener.h" 32 #include "EventNames.h" 33 #include "ExceptionCode.h" 34 #include "Frame.h" 35 #include "HTMLBRElement.h" 36 #include "HTMLCollection.h" 37 #include "HTMLDocument.h" 38 #include "HTMLElementFactory.h" 39 #include "HTMLFormElement.h" 40 #include "HTMLNames.h" 41 #include "HTMLTokenizer.h" 42 #include "MappedAttribute.h" 43 #include "RenderWordBreak.h" 44 #include "ScriptEventListener.h" 45 #include "Settings.h" 46 #include "Text.h" 47 #include "TextIterator.h" 48 #include "XMLTokenizer.h" 49 #include "markup.h" 50 #include <wtf/StdLibExtras.h> 51 52 namespace WebCore { 53 54 using namespace HTMLNames; 55 56 using std::min; 57 using std::max; 58 59 PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document* document) 60 { 61 return adoptRef(new HTMLElement(tagName, document, CreateElement)); 62 } 63 64 String HTMLElement::nodeName() const 65 { 66 // FIXME: Would be nice to have an atomicstring lookup based off uppercase chars that does not have to copy 67 // the string on a hit in the hash. 68 // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it. 69 if (document()->isHTMLDocument() && !tagQName().hasPrefix()) 70 return tagQName().localNameUpper(); 71 return Element::nodeName(); 72 } 73 74 HTMLTagStatus HTMLElement::endTagRequirement() const 75 { 76 if (hasLocalName(wbrTag)) 77 return TagStatusForbidden; 78 if (hasLocalName(dtTag) || hasLocalName(ddTag) || hasLocalName(rpTag) || hasLocalName(rtTag)) 79 return TagStatusOptional; 80 81 // Same values as <span>. This way custom tag name elements will behave like inline spans. 82 return TagStatusRequired; 83 } 84 85 struct Empty1IntHashTraits : HashTraits<int> { 86 static const bool emptyValueIsZero = false; 87 static int emptyValue() { return 1; } 88 }; 89 typedef HashMap<AtomicStringImpl*, int, DefaultHash<AtomicStringImpl*>::Hash, HashTraits<AtomicStringImpl*>, Empty1IntHashTraits> TagPriorityMap; 90 91 static const TagPriorityMap* createTagPriorityMap() 92 { 93 TagPriorityMap* map = new TagPriorityMap; 94 95 map->add(wbrTag.localName().impl(), 0); 96 97 map->add(addressTag.localName().impl(), 3); 98 map->add(ddTag.localName().impl(), 3); 99 map->add(dtTag.localName().impl(), 3); 100 map->add(noscriptTag.localName().impl(), 3); 101 map->add(rpTag.localName().impl(), 3); 102 map->add(rtTag.localName().impl(), 3); 103 104 // 5 is same as <div>'s priority. 105 map->add(articleTag.localName().impl(), 5); 106 map->add(asideTag.localName().impl(), 5); 107 map->add(centerTag.localName().impl(), 5); 108 map->add(footerTag.localName().impl(), 5); 109 map->add(headerTag.localName().impl(), 5); 110 map->add(nobrTag.localName().impl(), 5); 111 map->add(rubyTag.localName().impl(), 5); 112 map->add(navTag.localName().impl(), 5); 113 map->add(sectionTag.localName().impl(), 5); 114 115 map->add(noembedTag.localName().impl(), 10); 116 map->add(noframesTag.localName().impl(), 10); 117 118 // TagPriorityMap returns 1 for unregistered tags. It's same as <span>. 119 // This way custom tag name elements will behave like inline spans. 120 return map; 121 } 122 123 int HTMLElement::tagPriority() const 124 { 125 static const TagPriorityMap* tagPriorityMap = createTagPriorityMap(); 126 return tagPriorityMap->get(localName().impl()); 127 } 128 129 bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const 130 { 131 if (attrName == alignAttr || 132 attrName == contenteditableAttr) { 133 result = eUniversal; 134 return false; 135 } 136 if (attrName == dirAttr) { 137 result = hasLocalName(bdoTag) ? eBDO : eUniversal; 138 return false; 139 } 140 141 return StyledElement::mapToEntry(attrName, result); 142 } 143 144 void HTMLElement::parseMappedAttribute(MappedAttribute *attr) 145 { 146 if (attr->name() == idAttributeName() || attr->name() == classAttr || attr->name() == styleAttr) 147 return StyledElement::parseMappedAttribute(attr); 148 149 String indexstring; 150 if (attr->name() == alignAttr) { 151 if (equalIgnoringCase(attr->value(), "middle")) 152 addCSSProperty(attr, CSSPropertyTextAlign, "center"); 153 else 154 addCSSProperty(attr, CSSPropertyTextAlign, attr->value()); 155 } else if (attr->name() == contenteditableAttr) { 156 setContentEditable(attr); 157 } else if (attr->name() == tabindexAttr) { 158 indexstring = getAttribute(tabindexAttr); 159 if (indexstring.length()) { 160 bool parsedOK; 161 int tabindex = indexstring.toIntStrict(&parsedOK); 162 if (parsedOK) 163 // Clamp tabindex to the range of 'short' to match Firefox's behavior. 164 setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max())))); 165 } 166 } else if (attr->name() == langAttr) { 167 // FIXME: Implement 168 } else if (attr->name() == dirAttr) { 169 addCSSProperty(attr, CSSPropertyDirection, attr->value()); 170 addCSSProperty(attr, CSSPropertyUnicodeBidi, hasLocalName(bdoTag) ? CSSValueBidiOverride : CSSValueEmbed); 171 } else if (attr->name() == draggableAttr) { 172 const AtomicString& value = attr->value(); 173 if (equalIgnoringCase(value, "true")) { 174 addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueElement); 175 addCSSProperty(attr, CSSPropertyWebkitUserSelect, CSSValueNone); 176 } else if (equalIgnoringCase(value, "false")) 177 addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueNone); 178 } 179 // standard events 180 else if (attr->name() == onclickAttr) { 181 setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr)); 182 } else if (attr->name() == oncontextmenuAttr) { 183 setAttributeEventListener(eventNames().contextmenuEvent, createAttributeEventListener(this, attr)); 184 } else if (attr->name() == ondblclickAttr) { 185 setAttributeEventListener(eventNames().dblclickEvent, createAttributeEventListener(this, attr)); 186 } else if (attr->name() == onmousedownAttr) { 187 setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr)); 188 } else if (attr->name() == onmousemoveAttr) { 189 setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr)); 190 } else if (attr->name() == onmouseoutAttr) { 191 setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr)); 192 } else if (attr->name() == onmouseoverAttr) { 193 setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr)); 194 } else if (attr->name() == onmouseupAttr) { 195 setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr)); 196 } else if (attr->name() == onmousewheelAttr) { 197 setAttributeEventListener(eventNames().mousewheelEvent, createAttributeEventListener(this, attr)); 198 } else if (attr->name() == onfocusAttr) { 199 setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr)); 200 } else if (attr->name() == onblurAttr) { 201 setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr)); 202 } else if (attr->name() == onkeydownAttr) { 203 setAttributeEventListener(eventNames().keydownEvent, createAttributeEventListener(this, attr)); 204 } else if (attr->name() == onkeypressAttr) { 205 setAttributeEventListener(eventNames().keypressEvent, createAttributeEventListener(this, attr)); 206 } else if (attr->name() == onkeyupAttr) { 207 setAttributeEventListener(eventNames().keyupEvent, createAttributeEventListener(this, attr)); 208 } else if (attr->name() == onscrollAttr) { 209 setAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(this, attr)); 210 } else if (attr->name() == onbeforecutAttr) { 211 setAttributeEventListener(eventNames().beforecutEvent, createAttributeEventListener(this, attr)); 212 } else if (attr->name() == oncutAttr) { 213 setAttributeEventListener(eventNames().cutEvent, createAttributeEventListener(this, attr)); 214 } else if (attr->name() == onbeforecopyAttr) { 215 setAttributeEventListener(eventNames().beforecopyEvent, createAttributeEventListener(this, attr)); 216 } else if (attr->name() == oncopyAttr) { 217 setAttributeEventListener(eventNames().copyEvent, createAttributeEventListener(this, attr)); 218 } else if (attr->name() == onbeforepasteAttr) { 219 setAttributeEventListener(eventNames().beforepasteEvent, createAttributeEventListener(this, attr)); 220 } else if (attr->name() == onpasteAttr) { 221 setAttributeEventListener(eventNames().pasteEvent, createAttributeEventListener(this, attr)); 222 } else if (attr->name() == ondragenterAttr) { 223 setAttributeEventListener(eventNames().dragenterEvent, createAttributeEventListener(this, attr)); 224 } else if (attr->name() == ondragoverAttr) { 225 setAttributeEventListener(eventNames().dragoverEvent, createAttributeEventListener(this, attr)); 226 } else if (attr->name() == ondragleaveAttr) { 227 setAttributeEventListener(eventNames().dragleaveEvent, createAttributeEventListener(this, attr)); 228 } else if (attr->name() == ondropAttr) { 229 setAttributeEventListener(eventNames().dropEvent, createAttributeEventListener(this, attr)); 230 } else if (attr->name() == ondragstartAttr) { 231 setAttributeEventListener(eventNames().dragstartEvent, createAttributeEventListener(this, attr)); 232 } else if (attr->name() == ondragAttr) { 233 setAttributeEventListener(eventNames().dragEvent, createAttributeEventListener(this, attr)); 234 } else if (attr->name() == ondragendAttr) { 235 setAttributeEventListener(eventNames().dragendEvent, createAttributeEventListener(this, attr)); 236 } else if (attr->name() == onselectstartAttr) { 237 setAttributeEventListener(eventNames().selectstartEvent, createAttributeEventListener(this, attr)); 238 } else if (attr->name() == onsubmitAttr) { 239 setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr)); 240 } else if (attr->name() == onerrorAttr) { 241 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); 242 } else if (attr->name() == onwebkitanimationstartAttr) { 243 setAttributeEventListener(eventNames().webkitAnimationStartEvent, createAttributeEventListener(this, attr)); 244 } else if (attr->name() == onwebkitanimationiterationAttr) { 245 setAttributeEventListener(eventNames().webkitAnimationIterationEvent, createAttributeEventListener(this, attr)); 246 } else if (attr->name() == onwebkitanimationendAttr) { 247 setAttributeEventListener(eventNames().webkitAnimationEndEvent, createAttributeEventListener(this, attr)); 248 } else if (attr->name() == onwebkittransitionendAttr) { 249 setAttributeEventListener(eventNames().webkitTransitionEndEvent, createAttributeEventListener(this, attr)); 250 } else if (attr->name() == oninputAttr) { 251 setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, attr)); 252 } else if (attr->name() == oninvalidAttr) { 253 setAttributeEventListener(eventNames().invalidEvent, createAttributeEventListener(this, attr)); 254 } else if (attr->name() == ontouchstartAttr) { 255 setAttributeEventListener(eventNames().touchstartEvent, createAttributeEventListener(this, attr)); 256 } else if (attr->name() == ontouchmoveAttr) { 257 setAttributeEventListener(eventNames().touchmoveEvent, createAttributeEventListener(this, attr)); 258 } else if (attr->name() == ontouchendAttr) { 259 setAttributeEventListener(eventNames().touchendEvent, createAttributeEventListener(this, attr)); 260 } else if (attr->name() == ontouchcancelAttr) { 261 setAttributeEventListener(eventNames().touchcancelEvent, createAttributeEventListener(this, attr)); 262 } 263 } 264 265 String HTMLElement::innerHTML() const 266 { 267 return createMarkup(this, ChildrenOnly); 268 } 269 270 String HTMLElement::outerHTML() const 271 { 272 return createMarkup(this); 273 } 274 275 PassRefPtr<DocumentFragment> HTMLElement::createContextualFragment(const String &html, FragmentScriptingPermission scriptingPermission) 276 { 277 // the following is in accordance with the definition as used by IE 278 if (endTagRequirement() == TagStatusForbidden) 279 return 0; 280 281 if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || 282 hasLocalName(headTag) || hasLocalName(styleTag) || hasLocalName(titleTag)) 283 return 0; 284 285 RefPtr<DocumentFragment> fragment = DocumentFragment::create(document()); 286 287 if (document()->isHTMLDocument()) 288 parseHTMLDocumentFragment(html, fragment.get(), scriptingPermission); 289 else { 290 if (!parseXMLDocumentFragment(html, fragment.get(), this, scriptingPermission)) 291 // FIXME: We should propagate a syntax error exception out here. 292 return 0; 293 } 294 295 // Exceptions are ignored because none ought to happen here. 296 int ignoredExceptionCode; 297 298 // we need to pop <html> and <body> elements and remove <head> to 299 // accommodate folks passing complete HTML documents to make the 300 // child of an element. 301 302 RefPtr<Node> nextNode; 303 for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) { 304 nextNode = node->nextSibling(); 305 if (node->hasTagName(htmlTag) || node->hasTagName(bodyTag)) { 306 Node *firstChild = node->firstChild(); 307 if (firstChild) 308 nextNode = firstChild; 309 RefPtr<Node> nextChild; 310 for (RefPtr<Node> child = firstChild; child; child = nextChild) { 311 nextChild = child->nextSibling(); 312 node->removeChild(child.get(), ignoredExceptionCode); 313 ASSERT(!ignoredExceptionCode); 314 fragment->insertBefore(child, node.get(), ignoredExceptionCode); 315 ASSERT(!ignoredExceptionCode); 316 } 317 fragment->removeChild(node.get(), ignoredExceptionCode); 318 ASSERT(!ignoredExceptionCode); 319 } else if (node->hasTagName(headTag)) { 320 fragment->removeChild(node.get(), ignoredExceptionCode); 321 ASSERT(!ignoredExceptionCode); 322 } 323 } 324 325 return fragment.release(); 326 } 327 328 static inline bool hasOneChild(ContainerNode* node) 329 { 330 Node* firstChild = node->firstChild(); 331 return firstChild && !firstChild->nextSibling(); 332 } 333 334 static inline bool hasOneTextChild(ContainerNode* node) 335 { 336 return hasOneChild(node) && node->firstChild()->isTextNode(); 337 } 338 339 static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec) 340 { 341 if (!fragment->firstChild()) { 342 element->removeChildren(); 343 return; 344 } 345 346 if (hasOneTextChild(element) && hasOneTextChild(fragment.get())) { 347 static_cast<Text*>(element->firstChild())->setData(static_cast<Text*>(fragment->firstChild())->data(), ec); 348 return; 349 } 350 351 if (hasOneChild(element)) { 352 element->replaceChild(fragment, element->firstChild(), ec); 353 return; 354 } 355 356 element->removeChildren(); 357 element->appendChild(fragment, ec); 358 } 359 360 static void replaceChildrenWithText(HTMLElement* element, const String& text, ExceptionCode& ec) 361 { 362 if (hasOneTextChild(element)) { 363 static_cast<Text*>(element->firstChild())->setData(text, ec); 364 return; 365 } 366 367 RefPtr<Text> textNode = Text::create(element->document(), text); 368 369 if (hasOneChild(element)) { 370 element->replaceChild(textNode.release(), element->firstChild(), ec); 371 return; 372 } 373 374 element->removeChildren(); 375 element->appendChild(textNode.release(), ec); 376 } 377 378 void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec) 379 { 380 if (hasLocalName(scriptTag) || hasLocalName(styleTag)) { 381 // Script and CSS source shouldn't be parsed as HTML. 382 removeChildren(); 383 appendChild(document()->createTextNode(html), ec); 384 return; 385 } 386 387 RefPtr<DocumentFragment> fragment = createContextualFragment(html); 388 if (!fragment) { 389 ec = NO_MODIFICATION_ALLOWED_ERR; 390 return; 391 } 392 393 replaceChildrenWithFragment(this, fragment.release(), ec); 394 } 395 396 void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec) 397 { 398 Node* p = parent(); 399 if (!p || !p->isHTMLElement()) { 400 ec = NO_MODIFICATION_ALLOWED_ERR; 401 return; 402 } 403 404 HTMLElement* parent = static_cast<HTMLElement*>(p); 405 RefPtr<DocumentFragment> fragment = parent->createContextualFragment(html); 406 if (!fragment) { 407 ec = NO_MODIFICATION_ALLOWED_ERR; 408 return; 409 } 410 411 // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does? 412 413 parent->replaceChild(fragment.release(), this, ec); 414 } 415 416 void HTMLElement::setInnerText(const String& text, ExceptionCode& ec) 417 { 418 // follow the IE specs about when this is allowed 419 if (endTagRequirement() == TagStatusForbidden) { 420 ec = NO_MODIFICATION_ALLOWED_ERR; 421 return; 422 } 423 if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || 424 hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 425 hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || 426 hasLocalName(trTag)) { 427 ec = NO_MODIFICATION_ALLOWED_ERR; 428 return; 429 } 430 431 // FIXME: This doesn't take whitespace collapsing into account at all. 432 433 if (!text.contains('\n') && !text.contains('\r')) { 434 if (text.isEmpty()) { 435 removeChildren(); 436 return; 437 } 438 replaceChildrenWithText(this, text, ec); 439 return; 440 } 441 442 // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer? 443 // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded? 444 // For example, for the contents of textarea elements that are display:none? 445 RenderObject* r = renderer(); 446 if (r && r->style()->preserveNewline()) { 447 if (!text.contains('\r')) { 448 replaceChildrenWithText(this, text, ec); 449 return; 450 } 451 String textWithConsistentLineBreaks = text; 452 textWithConsistentLineBreaks.replace("\r\n", "\n"); 453 textWithConsistentLineBreaks.replace('\r', '\n'); 454 replaceChildrenWithText(this, textWithConsistentLineBreaks, ec); 455 return; 456 } 457 458 // Add text nodes and <br> elements. 459 ec = 0; 460 RefPtr<DocumentFragment> fragment = DocumentFragment::create(document()); 461 int lineStart = 0; 462 UChar prev = 0; 463 int length = text.length(); 464 for (int i = 0; i < length; ++i) { 465 UChar c = text[i]; 466 if (c == '\n' || c == '\r') { 467 if (i > lineStart) { 468 fragment->appendChild(Text::create(document(), text.substring(lineStart, i - lineStart)), ec); 469 if (ec) 470 return; 471 } 472 if (!(c == '\n' && i != 0 && prev == '\r')) { 473 fragment->appendChild(new HTMLBRElement(brTag, document()), ec); 474 if (ec) 475 return; 476 } 477 lineStart = i + 1; 478 } 479 prev = c; 480 } 481 if (length > lineStart) 482 fragment->appendChild(Text::create(document(), text.substring(lineStart, length - lineStart)), ec); 483 replaceChildrenWithFragment(this, fragment.release(), ec); 484 } 485 486 void HTMLElement::setOuterText(const String &text, ExceptionCode& ec) 487 { 488 // follow the IE specs about when this is allowed 489 if (endTagRequirement() == TagStatusForbidden) { 490 ec = NO_MODIFICATION_ALLOWED_ERR; 491 return; 492 } 493 if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || 494 hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 495 hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || 496 hasLocalName(trTag)) { 497 ec = NO_MODIFICATION_ALLOWED_ERR; 498 return; 499 } 500 501 Node* parent = parentNode(); 502 if (!parent) { 503 ec = NO_MODIFICATION_ALLOWED_ERR; 504 return; 505 } 506 507 // FIXME: This creates a new text node even when the text is empty. 508 // FIXME: This creates a single text node even when the text has CR and LF 509 // characters in it. Instead it should create <br> elements. 510 RefPtr<Text> t = Text::create(document(), text); 511 ec = 0; 512 parent->replaceChild(t, this, ec); 513 if (ec) 514 return; 515 516 // is previous node a text node? if so, merge into it 517 Node* prev = t->previousSibling(); 518 if (prev && prev->isTextNode()) { 519 Text* textPrev = static_cast<Text*>(prev); 520 textPrev->appendData(t->data(), ec); 521 if (ec) 522 return; 523 t->remove(ec); 524 if (ec) 525 return; 526 t = textPrev; 527 } 528 529 // is next node a text node? if so, merge it in 530 Node* next = t->nextSibling(); 531 if (next && next->isTextNode()) { 532 Text* textNext = static_cast<Text*>(next); 533 t->appendData(textNext->data(), ec); 534 if (ec) 535 return; 536 textNext->remove(ec); 537 if (ec) 538 return; 539 } 540 } 541 542 Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec) 543 { 544 // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", 545 // a document fragment is created and the elements appended in the correct order. This document 546 // fragment isn't returned anywhere. 547 // 548 // This is impossible for us to implement as the DOM tree does not allow for such structures, 549 // Opera also appears to disallow such usage. 550 551 if (equalIgnoringCase(where, "beforeBegin")) { 552 if (Node* p = parent()) 553 return p->insertBefore(newChild, this, ec) ? newChild : 0; 554 return 0; 555 } 556 557 if (equalIgnoringCase(where, "afterBegin")) 558 return insertBefore(newChild, firstChild(), ec) ? newChild : 0; 559 560 if (equalIgnoringCase(where, "beforeEnd")) 561 return appendChild(newChild, ec) ? newChild : 0; 562 563 if (equalIgnoringCase(where, "afterEnd")) { 564 if (Node* p = parent()) 565 return p->insertBefore(newChild, nextSibling(), ec) ? newChild : 0; 566 return 0; 567 } 568 569 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative 570 ec = NOT_SUPPORTED_ERR; 571 return 0; 572 } 573 574 Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec) 575 { 576 if (!newChild) { 577 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative 578 ec = TYPE_MISMATCH_ERR; 579 return 0; 580 } 581 582 Node* returnValue = insertAdjacent(where, newChild, ec); 583 ASSERT(!returnValue || returnValue->isElementNode()); 584 return static_cast<Element*>(returnValue); 585 } 586 587 void HTMLElement::insertAdjacentHTML(const String& where, const String& html, ExceptionCode& ec) 588 { 589 RefPtr<DocumentFragment> fragment = document()->createDocumentFragment(); 590 if (document()->isHTMLDocument()) 591 parseHTMLDocumentFragment(html, fragment.get()); 592 else { 593 if (!parseXMLDocumentFragment(html, fragment.get(), this)) 594 // FIXME: We should propagate a syntax error exception out here. 595 return; 596 } 597 598 insertAdjacent(where, fragment.get(), ec); 599 } 600 601 void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec) 602 { 603 RefPtr<Text> textNode = document()->createTextNode(text); 604 insertAdjacent(where, textNode.get(), ec); 605 } 606 607 void HTMLElement::addHTMLAlignment(MappedAttribute* attr) 608 { 609 addHTMLAlignmentToStyledElement(this, attr); 610 } 611 612 void HTMLElement::addHTMLAlignmentToStyledElement(StyledElement* element, MappedAttribute* attr) 613 { 614 // vertical alignment with respect to the current baseline of the text 615 // right or left means floating images 616 int floatValue = CSSValueInvalid; 617 int verticalAlignValue = CSSValueInvalid; 618 619 const AtomicString& alignment = attr->value(); 620 if (equalIgnoringCase(alignment, "absmiddle")) 621 verticalAlignValue = CSSValueMiddle; 622 else if (equalIgnoringCase(alignment, "absbottom")) 623 verticalAlignValue = CSSValueBottom; 624 else if (equalIgnoringCase(alignment, "left")) { 625 floatValue = CSSValueLeft; 626 verticalAlignValue = CSSValueTop; 627 } else if (equalIgnoringCase(alignment, "right")) { 628 floatValue = CSSValueRight; 629 verticalAlignValue = CSSValueTop; 630 } else if (equalIgnoringCase(alignment, "top")) 631 verticalAlignValue = CSSValueTop; 632 else if (equalIgnoringCase(alignment, "middle")) 633 verticalAlignValue = CSSValueWebkitBaselineMiddle; 634 else if (equalIgnoringCase(alignment, "center")) 635 verticalAlignValue = CSSValueMiddle; 636 else if (equalIgnoringCase(alignment, "bottom")) 637 verticalAlignValue = CSSValueBaseline; 638 else if (equalIgnoringCase(alignment, "texttop")) 639 verticalAlignValue = CSSValueTextTop; 640 641 if (floatValue != CSSValueInvalid) 642 element->addCSSProperty(attr, CSSPropertyFloat, floatValue); 643 644 if (verticalAlignValue != CSSValueInvalid) 645 element->addCSSProperty(attr, CSSPropertyVerticalAlign, verticalAlignValue); 646 } 647 648 bool HTMLElement::supportsFocus() const 649 { 650 return Element::supportsFocus() || (isContentEditable() && parent() && !parent()->isContentEditable()); 651 } 652 653 bool HTMLElement::isContentEditable() const 654 { 655 if (document()->frame() && document()->frame()->isContentEditable()) 656 return true; 657 658 // FIXME: this is a terrible thing to do here: 659 // https://bugs.webkit.org/show_bug.cgi?id=21834 660 document()->updateStyleIfNeeded(); 661 662 if (!renderer()) { 663 if (parentNode()) 664 return parentNode()->isContentEditable(); 665 else 666 return false; 667 } 668 669 return renderer()->style()->userModify() == READ_WRITE || renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY; 670 } 671 672 bool HTMLElement::isContentRichlyEditable() const 673 { 674 if (document()->frame() && document()->frame()->isContentEditable()) 675 return true; 676 677 document()->updateStyleIfNeeded(); 678 679 if (!renderer()) { 680 if (parentNode()) 681 return parentNode()->isContentEditable(); 682 else 683 return false; 684 } 685 686 return renderer()->style()->userModify() == READ_WRITE; 687 } 688 689 String HTMLElement::contentEditable() const 690 { 691 document()->updateStyleIfNeeded(); 692 693 if (!renderer()) 694 return "false"; 695 696 switch (renderer()->style()->userModify()) { 697 case READ_WRITE: 698 return "true"; 699 case READ_ONLY: 700 return "false"; 701 case READ_WRITE_PLAINTEXT_ONLY: 702 return "plaintext-only"; 703 default: 704 return "inherit"; 705 } 706 } 707 708 void HTMLElement::setContentEditable(MappedAttribute* attr) 709 { 710 const AtomicString& enabled = attr->value(); 711 if (enabled.isEmpty() || equalIgnoringCase(enabled, "true")) { 712 addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWrite); 713 addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord); 714 addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace); 715 addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); 716 } else if (equalIgnoringCase(enabled, "false")) { 717 addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadOnly); 718 attr->decl()->removeProperty(CSSPropertyWordWrap, false); 719 attr->decl()->removeProperty(CSSPropertyWebkitNbspMode, false); 720 attr->decl()->removeProperty(CSSPropertyWebkitLineBreak, false); 721 } else if (equalIgnoringCase(enabled, "inherit")) { 722 addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueInherit); 723 attr->decl()->removeProperty(CSSPropertyWordWrap, false); 724 attr->decl()->removeProperty(CSSPropertyWebkitNbspMode, false); 725 attr->decl()->removeProperty(CSSPropertyWebkitLineBreak, false); 726 } else if (equalIgnoringCase(enabled, "plaintext-only")) { 727 addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly); 728 addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord); 729 addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace); 730 addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); 731 } 732 } 733 734 void HTMLElement::setContentEditable(const String &enabled) 735 { 736 if (enabled == "inherit") { 737 ExceptionCode ec; 738 removeAttribute(contenteditableAttr, ec); 739 } 740 else 741 setAttribute(contenteditableAttr, enabled.isEmpty() ? "true" : enabled); 742 } 743 744 bool HTMLElement::draggable() const 745 { 746 return equalIgnoringCase(getAttribute(draggableAttr), "true"); 747 } 748 749 void HTMLElement::setDraggable(bool value) 750 { 751 setAttribute(draggableAttr, value ? "true" : "false"); 752 } 753 754 void HTMLElement::click() 755 { 756 dispatchSimulatedClick(0, false, false); 757 } 758 759 // accessKeyAction is used by the accessibility support code 760 // to send events to elements that our JavaScript caller does 761 // does not. The elements JS is interested in have subclasses 762 // that override this method to direct the click appropriately. 763 // Here in the base class, then, we only send the click if 764 // the caller wants it to go to any HTMLElement, and we say 765 // to send the mouse events in addition to the click. 766 void HTMLElement::accessKeyAction(bool sendToAnyElement) 767 { 768 if (sendToAnyElement) 769 dispatchSimulatedClick(0, true); 770 } 771 772 String HTMLElement::title() const 773 { 774 return getAttribute(titleAttr); 775 } 776 777 short HTMLElement::tabIndex() const 778 { 779 if (supportsFocus()) 780 return Element::tabIndex(); 781 return -1; 782 } 783 784 void HTMLElement::setTabIndex(int value) 785 { 786 setAttribute(tabindexAttr, String::number(value)); 787 } 788 789 PassRefPtr<HTMLCollection> HTMLElement::children() 790 { 791 return HTMLCollection::create(this, NodeChildren); 792 } 793 794 // DOM Section 1.1.1 795 bool HTMLElement::childAllowed(Node *newChild) 796 { 797 if (!Element::childAllowed(newChild)) 798 return false; 799 800 // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements. 801 if (!document()->isHTMLDocument()) 802 return true; 803 804 // Future-proof for XML content inside HTML documents (we may allow this some day). 805 if (newChild->isElementNode() && !newChild->isHTMLElement()) 806 return true; 807 808 // Elements with forbidden tag status can never have children 809 if (endTagRequirement() == TagStatusForbidden) 810 return false; 811 812 // Comment nodes are always allowed. 813 if (newChild->isCommentNode()) 814 return true; 815 816 // Now call checkDTD. 817 return checkDTD(newChild); 818 } 819 820 // DTD Stuff 821 // This unfortunate function is only needed when checking against the DTD. Other languages (like SVG) won't need this. 822 bool HTMLElement::isRecognizedTagName(const QualifiedName& tagName) 823 { 824 DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ()); 825 if (tagList.isEmpty()) { 826 size_t tagCount = 0; 827 WebCore::QualifiedName** tags = HTMLNames::getHTMLTags(&tagCount); 828 for (size_t i = 0; i < tagCount; i++) 829 tagList.add(tags[i]->localName().impl()); 830 } 831 return tagList.contains(tagName.localName().impl()); 832 } 833 834 // The terms inline and block are used here loosely. Don't make the mistake of assuming all inlines or all blocks 835 // need to be in these two lists. 836 static HashSet<AtomicStringImpl*>* inlineTagList() 837 { 838 DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ()); 839 if (tagList.isEmpty()) { 840 tagList.add(ttTag.localName().impl()); 841 tagList.add(iTag.localName().impl()); 842 tagList.add(bTag.localName().impl()); 843 tagList.add(uTag.localName().impl()); 844 tagList.add(sTag.localName().impl()); 845 tagList.add(strikeTag.localName().impl()); 846 tagList.add(bigTag.localName().impl()); 847 tagList.add(smallTag.localName().impl()); 848 tagList.add(emTag.localName().impl()); 849 tagList.add(strongTag.localName().impl()); 850 tagList.add(dfnTag.localName().impl()); 851 tagList.add(codeTag.localName().impl()); 852 tagList.add(sampTag.localName().impl()); 853 tagList.add(kbdTag.localName().impl()); 854 tagList.add(varTag.localName().impl()); 855 tagList.add(citeTag.localName().impl()); 856 tagList.add(abbrTag.localName().impl()); 857 tagList.add(acronymTag.localName().impl()); 858 tagList.add(aTag.localName().impl()); 859 tagList.add(canvasTag.localName().impl()); 860 tagList.add(imgTag.localName().impl()); 861 tagList.add(appletTag.localName().impl()); 862 tagList.add(objectTag.localName().impl()); 863 tagList.add(embedTag.localName().impl()); 864 tagList.add(fontTag.localName().impl()); 865 tagList.add(basefontTag.localName().impl()); 866 tagList.add(brTag.localName().impl()); 867 tagList.add(scriptTag.localName().impl()); 868 tagList.add(styleTag.localName().impl()); 869 tagList.add(linkTag.localName().impl()); 870 tagList.add(mapTag.localName().impl()); 871 tagList.add(qTag.localName().impl()); 872 tagList.add(subTag.localName().impl()); 873 tagList.add(supTag.localName().impl()); 874 tagList.add(spanTag.localName().impl()); 875 tagList.add(bdoTag.localName().impl()); 876 tagList.add(iframeTag.localName().impl()); 877 tagList.add(inputTag.localName().impl()); 878 tagList.add(keygenTag.localName().impl()); 879 tagList.add(selectTag.localName().impl()); 880 tagList.add(datagridTag.localName().impl()); 881 tagList.add(textareaTag.localName().impl()); 882 tagList.add(labelTag.localName().impl()); 883 tagList.add(buttonTag.localName().impl()); 884 tagList.add(datalistTag.localName().impl()); 885 tagList.add(insTag.localName().impl()); 886 tagList.add(delTag.localName().impl()); 887 tagList.add(nobrTag.localName().impl()); 888 tagList.add(wbrTag.localName().impl()); 889 #if ENABLE(VIDEO) 890 tagList.add(audioTag.localName().impl()); 891 tagList.add(videoTag.localName().impl()); 892 #endif 893 tagList.add(rpTag.localName().impl()); 894 tagList.add(rtTag.localName().impl()); 895 tagList.add(rubyTag.localName().impl()); 896 } 897 return &tagList; 898 } 899 900 static HashSet<AtomicStringImpl*>* blockTagList() 901 { 902 DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ()); 903 if (tagList.isEmpty()) { 904 tagList.add(addressTag.localName().impl()); 905 tagList.add(articleTag.localName().impl()); 906 tagList.add(asideTag.localName().impl()); 907 tagList.add(blockquoteTag.localName().impl()); 908 tagList.add(centerTag.localName().impl()); 909 tagList.add(ddTag.localName().impl()); 910 tagList.add(dirTag.localName().impl()); 911 tagList.add(divTag.localName().impl()); 912 tagList.add(dlTag.localName().impl()); 913 tagList.add(dtTag.localName().impl()); 914 tagList.add(fieldsetTag.localName().impl()); 915 tagList.add(footerTag.localName().impl()); 916 tagList.add(formTag.localName().impl()); 917 tagList.add(h1Tag.localName().impl()); 918 tagList.add(h2Tag.localName().impl()); 919 tagList.add(h3Tag.localName().impl()); 920 tagList.add(h4Tag.localName().impl()); 921 tagList.add(h5Tag.localName().impl()); 922 tagList.add(h6Tag.localName().impl()); 923 tagList.add(headerTag.localName().impl()); 924 tagList.add(hrTag.localName().impl()); 925 tagList.add(isindexTag.localName().impl()); 926 tagList.add(layerTag.localName().impl()); 927 tagList.add(liTag.localName().impl()); 928 tagList.add(listingTag.localName().impl()); 929 tagList.add(marqueeTag.localName().impl()); 930 tagList.add(menuTag.localName().impl()); 931 tagList.add(navTag.localName().impl()); 932 tagList.add(noembedTag.localName().impl()); 933 tagList.add(noframesTag.localName().impl()); 934 tagList.add(nolayerTag.localName().impl()); 935 tagList.add(noscriptTag.localName().impl()); 936 tagList.add(olTag.localName().impl()); 937 tagList.add(pTag.localName().impl()); 938 tagList.add(plaintextTag.localName().impl()); 939 tagList.add(preTag.localName().impl()); 940 tagList.add(sectionTag.localName().impl()); 941 tagList.add(tableTag.localName().impl()); 942 tagList.add(ulTag.localName().impl()); 943 tagList.add(xmpTag.localName().impl()); 944 } 945 return &tagList; 946 } 947 948 bool HTMLElement::inEitherTagList(const Node* newChild) 949 { 950 if (newChild->isTextNode()) 951 return true; 952 953 if (newChild->isHTMLElement()) { 954 const HTMLElement* child = static_cast<const HTMLElement*>(newChild); 955 if (inlineTagList()->contains(child->tagQName().localName().impl())) { 956 #if PLATFORM(MAC) 957 if (child->tagQName().localName() == styleTag) { 958 // Leopard Mail doesn't expect <style> to be in the body of the document, so don't allow it in that case. 959 // See <rdar://problem/6621310> 960 Settings* settings = newChild->document() ? newChild->document()->settings() : 0; 961 if (settings && settings->needsLeopardMailQuirks()) 962 return false; 963 } 964 #endif 965 return true; 966 } 967 if (blockTagList()->contains(child->tagQName().localName().impl())) 968 return true; 969 return !isRecognizedTagName(child->tagQName()); // Accept custom html tags 970 } 971 972 return false; 973 } 974 975 bool HTMLElement::inInlineTagList(const Node* newChild) 976 { 977 if (newChild->isTextNode()) 978 return true; 979 980 if (newChild->isHTMLElement()) { 981 const HTMLElement* child = static_cast<const HTMLElement*>(newChild); 982 if (inlineTagList()->contains(child->tagQName().localName().impl())) 983 return true; 984 return !isRecognizedTagName(child->tagQName()); // Accept custom html tags 985 } 986 987 return false; 988 } 989 990 bool HTMLElement::inBlockTagList(const Node* newChild) 991 { 992 if (newChild->isTextNode()) 993 return true; 994 995 if (newChild->isHTMLElement()) { 996 const HTMLElement* child = static_cast<const HTMLElement*>(newChild); 997 return (blockTagList()->contains(child->tagQName().localName().impl())); 998 } 999 1000 return false; 1001 } 1002 1003 bool HTMLElement::checkDTD(const Node* newChild) 1004 { 1005 if (hasLocalName(addressTag) && newChild->hasTagName(pTag)) 1006 return true; 1007 return inEitherTagList(newChild); 1008 } 1009 1010 bool HTMLElement::rendererIsNeeded(RenderStyle *style) 1011 { 1012 #if !ENABLE(XHTMLMP) 1013 if (hasLocalName(noscriptTag)) { 1014 Settings* settings = document()->settings(); 1015 if (settings && settings->isJavaScriptEnabled()) 1016 return false; 1017 } 1018 #endif 1019 return StyledElement::rendererIsNeeded(style); 1020 } 1021 1022 RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style) 1023 { 1024 if (hasLocalName(wbrTag)) 1025 return new (arena) RenderWordBreak(this); 1026 return RenderObject::createObject(this, style); 1027 } 1028 1029 HTMLFormElement* HTMLElement::findFormAncestor() const 1030 { 1031 for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) 1032 if (ancestor->hasTagName(formTag)) 1033 return static_cast<HTMLFormElement*>(ancestor); 1034 return 0; 1035 } 1036 1037 HTMLFormElement* HTMLElement::virtualForm() const 1038 { 1039 return findFormAncestor(); 1040 } 1041 1042 } // namespace WebCore 1043 1044 #ifndef NDEBUG 1045 1046 // For use in the debugger 1047 void dumpInnerHTML(WebCore::HTMLElement*); 1048 1049 void dumpInnerHTML(WebCore::HTMLElement* element) 1050 { 1051 printf("%s\n", element->innerHTML().ascii().data()); 1052 } 1053 #endif 1054