1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@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 * Copyright (C) 2011 Motorola Mobility. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "HTMLElement.h"
27
28#include "Attribute.h"
29#include "CSSParser.h"
30#include "CSSPropertyNames.h"
31#include "CSSValueKeywords.h"
32#include "CSSValuePool.h"
33#include "DOMSettableTokenList.h"
34#include "DocumentFragment.h"
35#include "Event.h"
36#include "EventListener.h"
37#include "EventNames.h"
38#include "ExceptionCode.h"
39#include "Frame.h"
40#include "FrameLoader.h"
41#include "HTMLBRElement.h"
42#include "HTMLCollection.h"
43#include "HTMLDocument.h"
44#include "HTMLElementFactory.h"
45#include "HTMLFormElement.h"
46#include "HTMLNames.h"
47#include "HTMLParserIdioms.h"
48#include "HTMLTemplateElement.h"
49#include "HTMLTextFormControlElement.h"
50#include "NodeTraversal.h"
51#include "RenderWordBreak.h"
52#include "ScriptController.h"
53#include "ScriptEventListener.h"
54#include "Settings.h"
55#include "StylePropertySet.h"
56#include "Text.h"
57#include "TextIterator.h"
58#include "XMLNames.h"
59#include "markup.h"
60#include <wtf/StdLibExtras.h>
61#include <wtf/text/CString.h>
62
63#if ENABLE(MICRODATA)
64#include "HTMLPropertiesCollection.h"
65#include "MicroDataItemValue.h"
66#endif
67
68namespace WebCore {
69
70using namespace HTMLNames;
71using namespace WTF;
72
73using std::min;
74using std::max;
75
76PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document* document)
77{
78    return adoptRef(new HTMLElement(tagName, document));
79}
80
81String HTMLElement::nodeName() const
82{
83    // FIXME: Would be nice to have an atomicstring lookup based off uppercase
84    // chars that does not have to copy the string on a hit in the hash.
85    // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it.
86    if (document()->isHTMLDocument() && !tagQName().hasPrefix())
87        return tagQName().localNameUpper();
88    return Element::nodeName();
89}
90
91bool HTMLElement::ieForbidsInsertHTML() const
92{
93    // FIXME: Supposedly IE disallows settting innerHTML, outerHTML
94    // and createContextualFragment on these tags.  We have no tests to
95    // verify this however, so this list could be totally wrong.
96    // This list was moved from the previous endTagRequirement() implementation.
97    // This is also called from editing and assumed to be the list of tags
98    // for which no end tag should be serialized. It's unclear if the list for
99    // IE compat and the list for serialization sanity are the same.
100    if (hasLocalName(areaTag)
101        || hasLocalName(baseTag)
102        || hasLocalName(basefontTag)
103        || hasLocalName(brTag)
104        || hasLocalName(colTag)
105        || hasLocalName(embedTag)
106        || hasLocalName(frameTag)
107        || hasLocalName(hrTag)
108        || hasLocalName(imageTag)
109        || hasLocalName(imgTag)
110        || hasLocalName(inputTag)
111        || hasLocalName(isindexTag)
112        || hasLocalName(linkTag)
113        || hasLocalName(metaTag)
114        || hasLocalName(paramTag)
115        || hasLocalName(sourceTag)
116        || hasLocalName(wbrTag))
117        return true;
118    // FIXME: I'm not sure why dashboard mode would want to change the
119    // serialization of <canvas>, that seems like a bad idea.
120#if ENABLE(DASHBOARD_SUPPORT)
121    if (hasLocalName(canvasTag)) {
122        Settings* settings = document()->settings();
123        if (settings && settings->usesDashboardBackwardCompatibilityMode())
124            return true;
125    }
126#endif
127    return false;
128}
129
130static inline int unicodeBidiAttributeForDirAuto(HTMLElement* element)
131{
132    if (element->hasLocalName(preTag) || element->hasLocalName(textareaTag))
133        return CSSValueWebkitPlaintext;
134    // FIXME: For bdo element, dir="auto" should result in "bidi-override isolate" but we don't support having multiple values in unicode-bidi yet.
135    // See https://bugs.webkit.org/show_bug.cgi?id=73164.
136    return CSSValueWebkitIsolate;
137}
138
139unsigned HTMLElement::parseBorderWidthAttribute(const AtomicString& value) const
140{
141    unsigned borderWidth = 0;
142    if (value.isEmpty() || !parseHTMLNonNegativeInteger(value, borderWidth))
143        return hasLocalName(tableTag) ? 1 : borderWidth;
144    return borderWidth;
145}
146
147void HTMLElement::applyBorderAttributeToStyle(const AtomicString& value, MutableStylePropertySet* style)
148{
149    addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, parseBorderWidthAttribute(value), CSSPrimitiveValue::CSS_PX);
150    addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderStyle, CSSValueSolid);
151}
152
153void HTMLElement::mapLanguageAttributeToLocale(const AtomicString& value, MutableStylePropertySet* style)
154{
155    if (!value.isEmpty()) {
156        // Have to quote so the locale id is treated as a string instead of as a CSS keyword.
157        addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, quoteCSSString(value));
158    } else {
159        // The empty string means the language is explicitly unknown.
160        addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, CSSValueAuto);
161    }
162}
163
164bool HTMLElement::isPresentationAttribute(const QualifiedName& name) const
165{
166    if (name == alignAttr || name == contenteditableAttr || name == hiddenAttr || name == langAttr || name.matches(XMLNames::langAttr) || name == draggableAttr || name == dirAttr)
167        return true;
168    return StyledElement::isPresentationAttribute(name);
169}
170
171void HTMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
172{
173    if (name == alignAttr) {
174        if (equalIgnoringCase(value, "middle"))
175            addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, CSSValueCenter);
176        else
177            addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, value);
178    } else if (name == contenteditableAttr) {
179        if (value.isEmpty() || equalIgnoringCase(value, "true")) {
180            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWrite);
181            addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord);
182            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitNbspMode, CSSValueSpace);
183            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
184        } else if (equalIgnoringCase(value, "plaintext-only")) {
185            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly);
186            addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord);
187            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitNbspMode, CSSValueSpace);
188            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
189        } else if (equalIgnoringCase(value, "false"))
190            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadOnly);
191    } else if (name == hiddenAttr) {
192        addPropertyToPresentationAttributeStyle(style, CSSPropertyDisplay, CSSValueNone);
193    } else if (name == draggableAttr) {
194        if (equalIgnoringCase(value, "true")) {
195            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueElement);
196            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserSelect, CSSValueNone);
197        } else if (equalIgnoringCase(value, "false"))
198            addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueNone);
199    } else if (name == dirAttr) {
200        if (equalIgnoringCase(value, "auto"))
201            addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(this));
202        else {
203            addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
204            if (!hasTagName(bdiTag) && !hasTagName(bdoTag) && !hasTagName(outputTag))
205                addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, CSSValueEmbed);
206        }
207    } else if (name.matches(XMLNames::langAttr))
208        mapLanguageAttributeToLocale(value, style);
209    else if (name == langAttr) {
210        // xml:lang has a higher priority than lang.
211        if (!fastHasAttribute(XMLNames::langAttr))
212            mapLanguageAttributeToLocale(value, style);
213    } else
214        StyledElement::collectStyleForPresentationAttribute(name, value, style);
215}
216
217AtomicString HTMLElement::eventNameForAttributeName(const QualifiedName& attrName) const
218{
219    if (!attrName.namespaceURI().isNull())
220        return AtomicString();
221
222    typedef HashMap<AtomicString, AtomicString> StringToStringMap;
223    DEFINE_STATIC_LOCAL(StringToStringMap, attributeNameToEventNameMap, ());
224    if (!attributeNameToEventNameMap.size()) {
225        attributeNameToEventNameMap.set(onclickAttr.localName(), eventNames().clickEvent);
226        attributeNameToEventNameMap.set(oncontextmenuAttr.localName(), eventNames().contextmenuEvent);
227        attributeNameToEventNameMap.set(ondblclickAttr.localName(), eventNames().dblclickEvent);
228        attributeNameToEventNameMap.set(onmousedownAttr.localName(), eventNames().mousedownEvent);
229        attributeNameToEventNameMap.set(onmouseenterAttr.localName(), eventNames().mouseenterEvent);
230        attributeNameToEventNameMap.set(onmouseleaveAttr.localName(), eventNames().mouseleaveEvent);
231        attributeNameToEventNameMap.set(onmousemoveAttr.localName(), eventNames().mousemoveEvent);
232        attributeNameToEventNameMap.set(onmouseoutAttr.localName(), eventNames().mouseoutEvent);
233        attributeNameToEventNameMap.set(onmouseoverAttr.localName(), eventNames().mouseoverEvent);
234        attributeNameToEventNameMap.set(onmouseupAttr.localName(), eventNames().mouseupEvent);
235        attributeNameToEventNameMap.set(onmousewheelAttr.localName(), eventNames().mousewheelEvent);
236        attributeNameToEventNameMap.set(onfocusAttr.localName(), eventNames().focusEvent);
237        attributeNameToEventNameMap.set(onfocusinAttr.localName(), eventNames().focusinEvent);
238        attributeNameToEventNameMap.set(onfocusoutAttr.localName(), eventNames().focusoutEvent);
239        attributeNameToEventNameMap.set(onblurAttr.localName(), eventNames().blurEvent);
240        attributeNameToEventNameMap.set(onkeydownAttr.localName(), eventNames().keydownEvent);
241        attributeNameToEventNameMap.set(onkeypressAttr.localName(), eventNames().keypressEvent);
242        attributeNameToEventNameMap.set(onkeyupAttr.localName(), eventNames().keyupEvent);
243        attributeNameToEventNameMap.set(onscrollAttr.localName(), eventNames().scrollEvent);
244        attributeNameToEventNameMap.set(onbeforecutAttr.localName(), eventNames().beforecutEvent);
245        attributeNameToEventNameMap.set(oncutAttr.localName(), eventNames().cutEvent);
246        attributeNameToEventNameMap.set(onbeforecopyAttr.localName(), eventNames().beforecopyEvent);
247        attributeNameToEventNameMap.set(oncopyAttr.localName(), eventNames().copyEvent);
248        attributeNameToEventNameMap.set(onbeforepasteAttr.localName(), eventNames().beforepasteEvent);
249        attributeNameToEventNameMap.set(onpasteAttr.localName(), eventNames().pasteEvent);
250        attributeNameToEventNameMap.set(ondragenterAttr.localName(), eventNames().dragenterEvent);
251        attributeNameToEventNameMap.set(ondragoverAttr.localName(), eventNames().dragoverEvent);
252        attributeNameToEventNameMap.set(ondragleaveAttr.localName(), eventNames().dragleaveEvent);
253        attributeNameToEventNameMap.set(ondropAttr.localName(), eventNames().dropEvent);
254        attributeNameToEventNameMap.set(ondragstartAttr.localName(), eventNames().dragstartEvent);
255        attributeNameToEventNameMap.set(ondragAttr.localName(), eventNames().dragEvent);
256        attributeNameToEventNameMap.set(ondragendAttr.localName(), eventNames().dragendEvent);
257        attributeNameToEventNameMap.set(onselectstartAttr.localName(), eventNames().selectstartEvent);
258        attributeNameToEventNameMap.set(onsubmitAttr.localName(), eventNames().submitEvent);
259        attributeNameToEventNameMap.set(onerrorAttr.localName(), eventNames().errorEvent);
260        attributeNameToEventNameMap.set(onwebkitanimationstartAttr.localName(), eventNames().webkitAnimationStartEvent);
261        attributeNameToEventNameMap.set(onwebkitanimationiterationAttr.localName(), eventNames().webkitAnimationIterationEvent);
262        attributeNameToEventNameMap.set(onwebkitanimationendAttr.localName(), eventNames().webkitAnimationEndEvent);
263        attributeNameToEventNameMap.set(onwebkittransitionendAttr.localName(), eventNames().webkitTransitionEndEvent);
264        attributeNameToEventNameMap.set(ontransitionendAttr.localName(), eventNames().webkitTransitionEndEvent);
265        attributeNameToEventNameMap.set(oninputAttr.localName(), eventNames().inputEvent);
266        attributeNameToEventNameMap.set(oninvalidAttr.localName(), eventNames().invalidEvent);
267        attributeNameToEventNameMap.set(ontouchstartAttr.localName(), eventNames().touchstartEvent);
268        attributeNameToEventNameMap.set(ontouchmoveAttr.localName(), eventNames().touchmoveEvent);
269        attributeNameToEventNameMap.set(ontouchendAttr.localName(), eventNames().touchendEvent);
270        attributeNameToEventNameMap.set(ontouchcancelAttr.localName(), eventNames().touchcancelEvent);
271#if ENABLE(FULLSCREEN_API)
272        attributeNameToEventNameMap.set(onwebkitfullscreenchangeAttr.localName(), eventNames().webkitfullscreenchangeEvent);
273        attributeNameToEventNameMap.set(onwebkitfullscreenerrorAttr.localName(), eventNames().webkitfullscreenerrorEvent);
274#endif
275        attributeNameToEventNameMap.set(onabortAttr.localName(), eventNames().abortEvent);
276        attributeNameToEventNameMap.set(oncanplayAttr.localName(), eventNames().canplayEvent);
277        attributeNameToEventNameMap.set(oncanplaythroughAttr.localName(), eventNames().canplaythroughEvent);
278        attributeNameToEventNameMap.set(onchangeAttr.localName(), eventNames().changeEvent);
279        attributeNameToEventNameMap.set(ondurationchangeAttr.localName(), eventNames().durationchangeEvent);
280        attributeNameToEventNameMap.set(onemptiedAttr.localName(), eventNames().emptiedEvent);
281        attributeNameToEventNameMap.set(onendedAttr.localName(), eventNames().endedEvent);
282        attributeNameToEventNameMap.set(onloadeddataAttr.localName(), eventNames().loadeddataEvent);
283        attributeNameToEventNameMap.set(onloadedmetadataAttr.localName(), eventNames().loadedmetadataEvent);
284        attributeNameToEventNameMap.set(onloadstartAttr.localName(), eventNames().loadstartEvent);
285        attributeNameToEventNameMap.set(onpauseAttr.localName(), eventNames().pauseEvent);
286        attributeNameToEventNameMap.set(onplayAttr.localName(), eventNames().playEvent);
287        attributeNameToEventNameMap.set(onplayingAttr.localName(), eventNames().playingEvent);
288        attributeNameToEventNameMap.set(onprogressAttr.localName(), eventNames().progressEvent);
289        attributeNameToEventNameMap.set(onratechangeAttr.localName(), eventNames().ratechangeEvent);
290        attributeNameToEventNameMap.set(onresetAttr.localName(), eventNames().resetEvent);
291        attributeNameToEventNameMap.set(onseekedAttr.localName(), eventNames().seekedEvent);
292        attributeNameToEventNameMap.set(onseekingAttr.localName(), eventNames().seekingEvent);
293        attributeNameToEventNameMap.set(onselectAttr.localName(), eventNames().selectEvent);
294        attributeNameToEventNameMap.set(onstalledAttr.localName(), eventNames().stalledEvent);
295        attributeNameToEventNameMap.set(onsuspendAttr.localName(), eventNames().suspendEvent);
296        attributeNameToEventNameMap.set(ontimeupdateAttr.localName(), eventNames().timeupdateEvent);
297        attributeNameToEventNameMap.set(onvolumechangeAttr.localName(), eventNames().volumechangeEvent);
298        attributeNameToEventNameMap.set(onwaitingAttr.localName(), eventNames().waitingEvent);
299        attributeNameToEventNameMap.set(onloadAttr.localName(), eventNames().loadEvent);
300    }
301
302    return attributeNameToEventNameMap.get(attrName.localName());
303}
304
305void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
306{
307    if (isIdAttributeName(name) || name == classAttr || name == styleAttr)
308        return StyledElement::parseAttribute(name, value);
309
310    if (name == dirAttr)
311        dirAttributeChanged(value);
312    else if (name == tabindexAttr) {
313        int tabindex = 0;
314        if (value.isEmpty())
315            clearTabIndexExplicitlyIfNeeded();
316        else if (parseHTMLInteger(value, tabindex)) {
317            // Clamp tabindex to the range of 'short' to match Firefox's behavior.
318            setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max()))));
319        }
320#if ENABLE(MICRODATA)
321    } else if (name == itempropAttr) {
322        setItemProp(value);
323    } else if (name == itemrefAttr) {
324        setItemRef(value);
325    } else if (name == itemtypeAttr) {
326        setItemType(value);
327#endif
328    } else {
329        AtomicString eventName = eventNameForAttributeName(name);
330        if (!eventName.isNull())
331            setAttributeEventListener(eventName, createAttributeEventListener(this, name, value));
332    }
333}
334
335String HTMLElement::innerHTML() const
336{
337    return createMarkup(this, ChildrenOnly);
338}
339
340String HTMLElement::outerHTML() const
341{
342    return createMarkup(this);
343}
344
345void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec)
346{
347    if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, this, AllowScriptingContent, ec)) {
348        ContainerNode* container = this;
349#if ENABLE(TEMPLATE_ELEMENT)
350        if (hasLocalName(templateTag))
351            container = toHTMLTemplateElement(this)->content();
352#endif
353        replaceChildrenWithFragment(container, fragment.release(), ec);
354    }
355}
356
357static void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionCode& ec)
358{
359    ASSERT(node && node->isTextNode());
360    Node* next = node->nextSibling();
361    if (!next || !next->isTextNode())
362        return;
363
364    RefPtr<Text> textNode = toText(node.get());
365    RefPtr<Text> textNext = toText(next);
366    textNode->appendData(textNext->data(), ec);
367    if (ec)
368        return;
369    if (textNext->parentNode()) // Might have been removed by mutation event.
370        textNext->remove(ec);
371}
372
373void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec)
374{
375    Node* p = parentNode();
376    if (!p || !p->isHTMLElement()) {
377        ec = NO_MODIFICATION_ALLOWED_ERR;
378        return;
379    }
380    RefPtr<HTMLElement> parent = toHTMLElement(p);
381    RefPtr<Node> prev = previousSibling();
382    RefPtr<Node> next = nextSibling();
383
384    RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, parent.get(), AllowScriptingContent, ec);
385    if (ec)
386        return;
387
388    parent->replaceChild(fragment.release(), this, ec);
389    RefPtr<Node> node = next ? next->previousSibling() : 0;
390    if (!ec && node && node->isTextNode())
391        mergeWithNextTextNode(node.release(), ec);
392
393    if (!ec && prev && prev->isTextNode())
394        mergeWithNextTextNode(prev.release(), ec);
395}
396
397PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionCode& ec)
398{
399    RefPtr<DocumentFragment> fragment = DocumentFragment::create(document());
400    unsigned int i, length = text.length();
401    UChar c = 0;
402    for (unsigned int start = 0; start < length; ) {
403
404        // Find next line break.
405        for (i = start; i < length; i++) {
406          c = text[i];
407          if (c == '\r' || c == '\n')
408              break;
409        }
410
411        fragment->appendChild(Text::create(document(), text.substring(start, i - start)), ec);
412        if (ec)
413            return 0;
414
415        if (c == '\r' || c == '\n') {
416            fragment->appendChild(HTMLBRElement::create(document()), ec);
417            if (ec)
418                return 0;
419            // Make sure \r\n doesn't result in two line breaks.
420            if (c == '\r' && i + 1 < length && text[i + 1] == '\n')
421                i++;
422        }
423
424        start = i + 1; // Character after line break.
425    }
426
427    return fragment;
428}
429
430void HTMLElement::setInnerText(const String& text, ExceptionCode& ec)
431{
432    if (ieForbidsInsertHTML()) {
433        ec = NO_MODIFICATION_ALLOWED_ERR;
434        return;
435    }
436    if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
437        hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) ||
438        hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
439        hasLocalName(trTag)) {
440        ec = NO_MODIFICATION_ALLOWED_ERR;
441        return;
442    }
443
444    // FIXME: This doesn't take whitespace collapsing into account at all.
445
446    if (!text.contains('\n') && !text.contains('\r')) {
447        if (text.isEmpty()) {
448            removeChildren();
449            return;
450        }
451        replaceChildrenWithText(this, text, ec);
452        return;
453    }
454
455    // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
456    // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
457    // For example, for the contents of textarea elements that are display:none?
458    RenderObject* r = renderer();
459    if (r && r->style()->preserveNewline()) {
460        if (!text.contains('\r')) {
461            replaceChildrenWithText(this, text, ec);
462            return;
463        }
464        String textWithConsistentLineBreaks = text;
465        textWithConsistentLineBreaks.replace("\r\n", "\n");
466        textWithConsistentLineBreaks.replace('\r', '\n');
467        replaceChildrenWithText(this, textWithConsistentLineBreaks, ec);
468        return;
469    }
470
471    // Add text nodes and <br> elements.
472    ec = 0;
473    RefPtr<DocumentFragment> fragment = textToFragment(text, ec);
474    if (!ec)
475        replaceChildrenWithFragment(this, fragment.release(), ec);
476}
477
478void HTMLElement::setOuterText(const String &text, ExceptionCode& ec)
479{
480    if (ieForbidsInsertHTML()) {
481        ec = NO_MODIFICATION_ALLOWED_ERR;
482        return;
483    }
484    if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
485        hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) ||
486        hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
487        hasLocalName(trTag)) {
488        ec = NO_MODIFICATION_ALLOWED_ERR;
489        return;
490    }
491
492    ContainerNode* parent = parentNode();
493    if (!parent) {
494        ec = NO_MODIFICATION_ALLOWED_ERR;
495        return;
496    }
497
498    RefPtr<Node> prev = previousSibling();
499    RefPtr<Node> next = nextSibling();
500    RefPtr<Node> newChild;
501    ec = 0;
502
503    // Convert text to fragment with <br> tags instead of linebreaks if needed.
504    if (text.contains('\r') || text.contains('\n'))
505        newChild = textToFragment(text, ec);
506    else
507        newChild = Text::create(document(), text);
508
509    if (!this || !parentNode())
510        ec = HIERARCHY_REQUEST_ERR;
511    if (ec)
512        return;
513    parent->replaceChild(newChild.release(), this, ec);
514
515    RefPtr<Node> node = next ? next->previousSibling() : 0;
516    if (!ec && node && node->isTextNode())
517        mergeWithNextTextNode(node.release(), ec);
518
519    if (!ec && prev && prev->isTextNode())
520        mergeWithNextTextNode(prev.release(), ec);
521}
522
523Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec)
524{
525    // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
526    // a document fragment is created and the elements appended in the correct order. This document
527    // fragment isn't returned anywhere.
528    //
529    // This is impossible for us to implement as the DOM tree does not allow for such structures,
530    // Opera also appears to disallow such usage.
531
532    if (equalIgnoringCase(where, "beforeBegin")) {
533        ContainerNode* parent = this->parentNode();
534        return (parent && parent->insertBefore(newChild, this, ec)) ? newChild : 0;
535    }
536
537    if (equalIgnoringCase(where, "afterBegin"))
538        return insertBefore(newChild, firstChild(), ec) ? newChild : 0;
539
540    if (equalIgnoringCase(where, "beforeEnd"))
541        return appendChild(newChild, ec) ? newChild : 0;
542
543    if (equalIgnoringCase(where, "afterEnd")) {
544        ContainerNode* parent = this->parentNode();
545        return (parent && parent->insertBefore(newChild, nextSibling(), ec)) ? newChild : 0;
546    }
547
548    // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative.
549    ec = NOT_SUPPORTED_ERR;
550    return 0;
551}
552
553Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec)
554{
555    if (!newChild) {
556        // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative.
557        ec = TYPE_MISMATCH_ERR;
558        return 0;
559    }
560
561    Node* returnValue = insertAdjacent(where, newChild, ec);
562    ASSERT_WITH_SECURITY_IMPLICATION(!returnValue || returnValue->isElementNode());
563    return toElement(returnValue);
564}
565
566// Step 3 of http://www.whatwg.org/specs/web-apps/current-work/multipage/apis-in-html-documents.html#insertadjacenthtml()
567static Element* contextElementForInsertion(const String& where, Element* element, ExceptionCode& ec)
568{
569    if (equalIgnoringCase(where, "beforeBegin") || equalIgnoringCase(where, "afterEnd")) {
570        ContainerNode* parent = element->parentNode();
571        if (parent && !parent->isElementNode()) {
572            ec = NO_MODIFICATION_ALLOWED_ERR;
573            return 0;
574        }
575        ASSERT_WITH_SECURITY_IMPLICATION(!parent || parent->isElementNode());
576        return toElement(parent);
577    }
578    if (equalIgnoringCase(where, "afterBegin") || equalIgnoringCase(where, "beforeEnd"))
579        return element;
580    ec =  SYNTAX_ERR;
581    return 0;
582}
583
584void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, ExceptionCode& ec)
585{
586    Element* contextElement = contextElementForInsertion(where, this, ec);
587    if (!contextElement)
588        return;
589    RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, contextElement, AllowScriptingContent, ec);
590    if (!fragment)
591        return;
592    insertAdjacent(where, fragment.get(), ec);
593}
594
595void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec)
596{
597    RefPtr<Text> textNode = document()->createTextNode(text);
598    insertAdjacent(where, textNode.get(), ec);
599}
600
601void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, MutableStylePropertySet* style)
602{
603    // Vertical alignment with respect to the current baseline of the text
604    // right or left means floating images.
605    int floatValue = CSSValueInvalid;
606    int verticalAlignValue = CSSValueInvalid;
607
608    if (equalIgnoringCase(alignment, "absmiddle"))
609        verticalAlignValue = CSSValueMiddle;
610    else if (equalIgnoringCase(alignment, "absbottom"))
611        verticalAlignValue = CSSValueBottom;
612    else if (equalIgnoringCase(alignment, "left")) {
613        floatValue = CSSValueLeft;
614        verticalAlignValue = CSSValueTop;
615    } else if (equalIgnoringCase(alignment, "right")) {
616        floatValue = CSSValueRight;
617        verticalAlignValue = CSSValueTop;
618    } else if (equalIgnoringCase(alignment, "top"))
619        verticalAlignValue = CSSValueTop;
620    else if (equalIgnoringCase(alignment, "middle"))
621        verticalAlignValue = CSSValueWebkitBaselineMiddle;
622    else if (equalIgnoringCase(alignment, "center"))
623        verticalAlignValue = CSSValueMiddle;
624    else if (equalIgnoringCase(alignment, "bottom"))
625        verticalAlignValue = CSSValueBaseline;
626    else if (equalIgnoringCase(alignment, "texttop"))
627        verticalAlignValue = CSSValueTextTop;
628
629    if (floatValue != CSSValueInvalid)
630        addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, floatValue);
631
632    if (verticalAlignValue != CSSValueInvalid)
633        addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, verticalAlignValue);
634}
635
636bool HTMLElement::hasCustomFocusLogic() const
637{
638    return false;
639}
640
641bool HTMLElement::supportsFocus() const
642{
643    return Element::supportsFocus() || (rendererIsEditable() && parentNode() && !parentNode()->rendererIsEditable());
644}
645
646String HTMLElement::contentEditable() const
647{
648    const AtomicString& value = fastGetAttribute(contenteditableAttr);
649
650    if (value.isNull())
651        return "inherit";
652    if (value.isEmpty() || equalIgnoringCase(value, "true"))
653        return "true";
654    if (equalIgnoringCase(value, "false"))
655         return "false";
656    if (equalIgnoringCase(value, "plaintext-only"))
657        return "plaintext-only";
658
659    return "inherit";
660}
661
662void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec)
663{
664    if (equalIgnoringCase(enabled, "true"))
665        setAttribute(contenteditableAttr, "true");
666    else if (equalIgnoringCase(enabled, "false"))
667        setAttribute(contenteditableAttr, "false");
668    else if (equalIgnoringCase(enabled, "plaintext-only"))
669        setAttribute(contenteditableAttr, "plaintext-only");
670    else if (equalIgnoringCase(enabled, "inherit"))
671        removeAttribute(contenteditableAttr);
672    else
673        ec = SYNTAX_ERR;
674}
675
676bool HTMLElement::draggable() const
677{
678    return equalIgnoringCase(getAttribute(draggableAttr), "true");
679}
680
681void HTMLElement::setDraggable(bool value)
682{
683    setAttribute(draggableAttr, value ? "true" : "false");
684}
685
686bool HTMLElement::spellcheck() const
687{
688    return isSpellCheckingEnabled();
689}
690
691void HTMLElement::setSpellcheck(bool enable)
692{
693    setAttribute(spellcheckAttr, enable ? "true" : "false");
694}
695
696
697void HTMLElement::click()
698{
699    dispatchSimulatedClick(0, SendNoEvents, DoNotShowPressedLook);
700}
701
702void HTMLElement::accessKeyAction(bool sendMouseEvents)
703{
704    dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
705}
706
707String HTMLElement::title() const
708{
709    return getAttribute(titleAttr);
710}
711
712short HTMLElement::tabIndex() const
713{
714    if (supportsFocus())
715        return Element::tabIndex();
716    return -1;
717}
718
719void HTMLElement::setTabIndex(int value)
720{
721    setAttribute(tabindexAttr, String::number(value));
722}
723
724TranslateAttributeMode HTMLElement::translateAttributeMode() const
725{
726    const AtomicString& value = getAttribute(translateAttr);
727
728    if (value == nullAtom)
729        return TranslateAttributeInherit;
730    if (equalIgnoringCase(value, "yes") || equalIgnoringCase(value, ""))
731        return TranslateAttributeYes;
732    if (equalIgnoringCase(value, "no"))
733        return TranslateAttributeNo;
734
735    return TranslateAttributeInherit;
736}
737
738bool HTMLElement::translate() const
739{
740    for (const Node* n = this; n; n = n->parentNode()) {
741        if (n->isHTMLElement()) {
742            TranslateAttributeMode mode = static_cast<const HTMLElement*>(n)->translateAttributeMode();
743            if (mode != TranslateAttributeInherit) {
744                ASSERT(mode == TranslateAttributeYes || mode == TranslateAttributeNo);
745                return mode == TranslateAttributeYes;
746            }
747        }
748    }
749
750    // Default on the root element is translate=yes.
751    return true;
752}
753
754void HTMLElement::setTranslate(bool enable)
755{
756    setAttribute(translateAttr, enable ? "yes" : "no");
757}
758
759
760PassRefPtr<HTMLCollection> HTMLElement::children()
761{
762    return ensureCachedHTMLCollection(NodeChildren);
763}
764
765bool HTMLElement::rendererIsNeeded(const NodeRenderingContext& context)
766{
767    if (hasLocalName(noscriptTag)) {
768        Frame* frame = document()->frame();
769        if (frame && frame->script()->canExecuteScripts(NotAboutToExecuteScript))
770            return false;
771    } else if (hasLocalName(noembedTag)) {
772        Frame* frame = document()->frame();
773        if (frame && frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin))
774            return false;
775    }
776    return StyledElement::rendererIsNeeded(context);
777}
778
779RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style)
780{
781    if (hasLocalName(wbrTag))
782        return new (arena) RenderWordBreak(this);
783    return RenderObject::createObject(this, style);
784}
785
786HTMLFormElement* HTMLElement::findFormAncestor() const
787{
788    for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
789        if (ancestor->hasTagName(formTag))
790            return static_cast<HTMLFormElement*>(ancestor);
791    }
792    return 0;
793}
794
795HTMLFormElement* HTMLElement::virtualForm() const
796{
797    return findFormAncestor();
798}
799
800static inline bool elementAffectsDirectionality(const Node* node)
801{
802    return node->isHTMLElement() && (node->hasTagName(bdiTag) || toHTMLElement(node)->hasAttribute(dirAttr));
803}
804
805static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = 0)
806{
807    firstNode->setSelfOrAncestorHasDirAutoAttribute(flag);
808
809    Node* node = firstNode->firstChild();
810
811    while (node) {
812        if (node->selfOrAncestorHasDirAutoAttribute() == flag)
813            return;
814
815        if (elementAffectsDirectionality(node)) {
816            if (node == lastNode)
817                return;
818            node = NodeTraversal::nextSkippingChildren(node, firstNode);
819            continue;
820        }
821        node->setSelfOrAncestorHasDirAutoAttribute(flag);
822        if (node == lastNode)
823            return;
824        node = NodeTraversal::next(node, firstNode);
825    }
826}
827
828void HTMLElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
829{
830    StyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
831    adjustDirectionalityIfNeededAfterChildrenChanged(beforeChange, childCountDelta);
832}
833
834bool HTMLElement::hasDirectionAuto() const
835{
836    const AtomicString& direction = fastGetAttribute(dirAttr);
837    return (hasTagName(bdiTag) && direction == nullAtom) || equalIgnoringCase(direction, "auto");
838}
839
840TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const
841{
842    if (!(selfOrAncestorHasDirAutoAttribute() && hasDirectionAuto())) {
843        isAuto = false;
844        return LTR;
845    }
846
847    isAuto = true;
848    return directionality();
849}
850
851TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const
852{
853    if (isHTMLTextFormControlElement(this)) {
854        HTMLTextFormControlElement* textElement = toHTMLTextFormControlElement(const_cast<HTMLElement*>(this));
855        bool hasStrongDirectionality;
856        Unicode::Direction textDirection = textElement->value().defaultWritingDirection(&hasStrongDirectionality);
857        if (strongDirectionalityTextNode)
858            *strongDirectionalityTextNode = hasStrongDirectionality ? textElement : 0;
859        return (textDirection == Unicode::LeftToRight) ? LTR : RTL;
860    }
861
862    Node* node = firstChild();
863    while (node) {
864        // Skip bdi, script, style and text form controls.
865        if (equalIgnoringCase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag)
866            || (node->isElementNode() && toElement(node)->isTextFormControl())) {
867            node = NodeTraversal::nextSkippingChildren(node, this);
868            continue;
869        }
870
871        // Skip elements with valid dir attribute
872        if (node->isElementNode()) {
873            AtomicString dirAttributeValue = toElement(node)->fastGetAttribute(dirAttr);
874            if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr") || equalIgnoringCase(dirAttributeValue, "auto")) {
875                node = NodeTraversal::nextSkippingChildren(node, this);
876                continue;
877            }
878        }
879
880        if (node->isTextNode()) {
881            bool hasStrongDirectionality;
882            WTF::Unicode::Direction textDirection = node->textContent(true).defaultWritingDirection(&hasStrongDirectionality);
883            if (hasStrongDirectionality) {
884                if (strongDirectionalityTextNode)
885                    *strongDirectionalityTextNode = node;
886                return (textDirection == WTF::Unicode::LeftToRight) ? LTR : RTL;
887            }
888        }
889        node = NodeTraversal::next(node, this);
890    }
891    if (strongDirectionalityTextNode)
892        *strongDirectionalityTextNode = 0;
893    return LTR;
894}
895
896void HTMLElement::dirAttributeChanged(const AtomicString& value)
897{
898    Element* parent = parentElement();
899
900    if (parent && parent->isHTMLElement() && parent->selfOrAncestorHasDirAutoAttribute())
901        toHTMLElement(parent)->adjustDirectionalityIfNeededAfterChildAttributeChanged(this);
902
903    if (equalIgnoringCase(value, "auto"))
904        calculateAndAdjustDirectionality();
905}
906
907void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
908{
909    ASSERT(selfOrAncestorHasDirAutoAttribute());
910    Node* strongDirectionalityTextNode;
911    TextDirection textDirection = directionality(&strongDirectionalityTextNode);
912    setHasDirAutoFlagRecursively(child, false);
913    if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection) {
914        Element* elementToAdjust = this;
915        for (; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
916            if (elementAffectsDirectionality(elementToAdjust)) {
917                elementToAdjust->setNeedsStyleRecalc();
918                return;
919            }
920        }
921    }
922}
923
924void HTMLElement::calculateAndAdjustDirectionality()
925{
926    Node* strongDirectionalityTextNode;
927    TextDirection textDirection = directionality(&strongDirectionalityTextNode);
928    setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode);
929    if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection)
930        setNeedsStyleRecalc();
931}
932
933void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Node* beforeChange, int childCountDelta)
934{
935    if ((!document() || document()->renderer()) && childCountDelta < 0) {
936        Node* node = beforeChange ? NodeTraversal::nextSkippingChildren(beforeChange) : 0;
937        for (int counter = 0; node && counter < childCountDelta; counter++, node = NodeTraversal::nextSkippingChildren(node)) {
938            if (elementAffectsDirectionality(node))
939                continue;
940
941            setHasDirAutoFlagRecursively(node, false);
942        }
943    }
944
945    if (!selfOrAncestorHasDirAutoAttribute())
946        return;
947
948    Node* oldMarkedNode = beforeChange ? NodeTraversal::nextSkippingChildren(beforeChange) : 0;
949    while (oldMarkedNode && elementAffectsDirectionality(oldMarkedNode))
950        oldMarkedNode = NodeTraversal::nextSkippingChildren(oldMarkedNode, this);
951    if (oldMarkedNode)
952        setHasDirAutoFlagRecursively(oldMarkedNode, false);
953
954    for (Element* elementToAdjust = this; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
955        if (elementAffectsDirectionality(elementToAdjust)) {
956            toHTMLElement(elementToAdjust)->calculateAndAdjustDirectionality();
957            return;
958        }
959    }
960}
961
962bool HTMLElement::isURLAttribute(const Attribute& attribute) const
963{
964#if ENABLE(MICRODATA)
965    if (attribute.name() == itemidAttr)
966        return true;
967#endif
968    return StyledElement::isURLAttribute(attribute);
969}
970
971#if ENABLE(MICRODATA)
972void HTMLElement::setItemValue(const String& value, ExceptionCode& ec)
973{
974    if (!hasAttribute(itempropAttr) || hasAttribute(itemscopeAttr)) {
975        ec = INVALID_ACCESS_ERR;
976        return;
977    }
978
979    setItemValueText(value, ec);
980}
981
982PassRefPtr<MicroDataItemValue> HTMLElement::itemValue() const
983{
984    if (!hasAttribute(itempropAttr))
985        return 0;
986
987    if (hasAttribute(itemscopeAttr))
988        return MicroDataItemValue::createFromNode(const_cast<HTMLElement* const>(this));
989
990    return MicroDataItemValue::createFromString(itemValueText());
991}
992
993String HTMLElement::itemValueText() const
994{
995    return textContent(true);
996}
997
998void HTMLElement::setItemValueText(const String& value, ExceptionCode& ec)
999{
1000    setTextContent(value, ec);
1001}
1002
1003PassRefPtr<HTMLPropertiesCollection> HTMLElement::properties()
1004{
1005    return static_cast<HTMLPropertiesCollection*>(ensureCachedHTMLCollection(ItemProperties).get());
1006}
1007
1008void HTMLElement::getItemRefElements(Vector<HTMLElement*>& itemRefElements)
1009{
1010    if (!fastHasAttribute(itemscopeAttr))
1011        return;
1012
1013    if (!fastHasAttribute(itemrefAttr) || !itemRef()->length()) {
1014        itemRefElements.append(this);
1015        return;
1016    }
1017
1018    DOMSettableTokenList* itemRefs = itemRef();
1019    RefPtr<DOMSettableTokenList> processedItemRef = DOMSettableTokenList::create();
1020
1021    Node* rootNode;
1022    if (inDocument())
1023        rootNode = document();
1024    else {
1025        rootNode = this;
1026        while (Node* parent = rootNode->parentNode())
1027            rootNode = parent;
1028    }
1029
1030    for (Node* current = rootNode; current; current = NodeTraversal::next(current, rootNode)) {
1031        if (!current->isHTMLElement())
1032            continue;
1033        HTMLElement* element = toHTMLElement(current);
1034
1035        if (element == this) {
1036            itemRefElements.append(element);
1037            continue;
1038        }
1039
1040        const AtomicString& id = element->getIdAttribute();
1041        if (!processedItemRef->tokens().contains(id) && itemRefs->tokens().contains(id)) {
1042            processedItemRef->setValue(id);
1043            if (!element->isDescendantOf(this))
1044                itemRefElements.append(element);
1045        }
1046    }
1047}
1048#endif
1049
1050void HTMLElement::addHTMLLengthToStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& value)
1051{
1052    // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
1053    // length unit and make the appropriate parsed value.
1054
1055    // strip attribute garbage..
1056    StringImpl* v = value.impl();
1057    if (v) {
1058        unsigned int l = 0;
1059
1060        while (l < v->length() && (*v)[l] <= ' ')
1061            l++;
1062
1063        for (; l < v->length(); l++) {
1064            UChar cc = (*v)[l];
1065            if (cc > '9')
1066                break;
1067            if (cc < '0') {
1068                if (cc == '%' || cc == '*')
1069                    l++;
1070                if (cc != '.')
1071                    break;
1072            }
1073        }
1074
1075        if (l != v->length()) {
1076            addPropertyToPresentationAttributeStyle(style, propertyID, v->substring(0, l));
1077            return;
1078        }
1079    }
1080
1081    addPropertyToPresentationAttributeStyle(style, propertyID, value);
1082}
1083
1084static RGBA32 parseColorStringWithCrazyLegacyRules(const String& colorString)
1085{
1086    // Per spec, only look at the first 128 digits of the string.
1087    const size_t maxColorLength = 128;
1088    // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
1089    Vector<char, maxColorLength+2> digitBuffer;
1090
1091    size_t i = 0;
1092    // Skip a leading #.
1093    if (colorString[0] == '#')
1094        i = 1;
1095
1096    // Grab the first 128 characters, replacing non-hex characters with 0.
1097    // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
1098    for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
1099        if (!isASCIIHexDigit(colorString[i]))
1100            digitBuffer.append('0');
1101        else
1102            digitBuffer.append(colorString[i]);
1103    }
1104
1105    if (!digitBuffer.size())
1106        return Color::black;
1107
1108    // Pad the buffer out to at least the next multiple of three in size.
1109    digitBuffer.append('0');
1110    digitBuffer.append('0');
1111
1112    if (digitBuffer.size() < 6)
1113        return makeRGB(toASCIIHexValue(digitBuffer[0]), toASCIIHexValue(digitBuffer[1]), toASCIIHexValue(digitBuffer[2]));
1114
1115    // Split the digits into three components, then search the last 8 digits of each component.
1116    ASSERT(digitBuffer.size() >= 6);
1117    size_t componentLength = digitBuffer.size() / 3;
1118    size_t componentSearchWindowLength = min<size_t>(componentLength, 8);
1119    size_t redIndex = componentLength - componentSearchWindowLength;
1120    size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
1121    size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
1122    // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
1123    while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
1124        redIndex++;
1125        greenIndex++;
1126        blueIndex++;
1127    }
1128    ASSERT(redIndex + 1 < componentLength);
1129    ASSERT(greenIndex >= componentLength);
1130    ASSERT(greenIndex + 1 < componentLength * 2);
1131    ASSERT(blueIndex >= componentLength * 2);
1132    ASSERT_WITH_SECURITY_IMPLICATION(blueIndex + 1 < digitBuffer.size());
1133
1134    int redValue = toASCIIHexValue(digitBuffer[redIndex], digitBuffer[redIndex + 1]);
1135    int greenValue = toASCIIHexValue(digitBuffer[greenIndex], digitBuffer[greenIndex + 1]);
1136    int blueValue = toASCIIHexValue(digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);
1137    return makeRGB(redValue, greenValue, blueValue);
1138}
1139
1140// Color parsing that matches HTML's "rules for parsing a legacy color value"
1141void HTMLElement::addHTMLColorToStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& attributeValue)
1142{
1143    // An empty string doesn't apply a color. (One containing only whitespace does, which is why this check occurs before stripping.)
1144    if (attributeValue.isEmpty())
1145        return;
1146
1147    String colorString = attributeValue.stripWhiteSpace();
1148
1149    // "transparent" doesn't apply a color either.
1150    if (equalIgnoringCase(colorString, "transparent"))
1151        return;
1152
1153    // If the string is a named CSS color or a 3/6-digit hex color, use that.
1154    Color parsedColor(colorString);
1155    if (!parsedColor.isValid())
1156        parsedColor.setRGB(parseColorStringWithCrazyLegacyRules(colorString));
1157
1158    style->setProperty(propertyID, cssValuePool().createColorValue(parsedColor.rgb()));
1159}
1160
1161} // namespace WebCore
1162
1163#ifndef NDEBUG
1164
1165// For use in the debugger
1166void dumpInnerHTML(WebCore::HTMLElement*);
1167
1168void dumpInnerHTML(WebCore::HTMLElement* element)
1169{
1170    printf("%s\n", element->innerHTML().ascii().data());
1171}
1172#endif
1173