Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * (C) 2001 Dirk Mueller (mueller@kde.org) | 4 * (C) 2001 Dirk Mueller (mueller@kde.org) |
| 5 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) | 5 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
| 6 * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. | 6 * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. |
| 7 * Copyright (C) 2010 Google Inc. All rights reserved. | 7 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 8 * Copyright (C) 2011 Motorola Mobility, Inc. All rights reserved. | 8 * Copyright (C) 2011 Motorola Mobility, Inc. All rights reserved. |
| 9 * | 9 * |
| 10 * This library is free software; you can redistribute it and/or | 10 * This library is free software; you can redistribute it and/or |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 #include "config.h" | 27 #include "config.h" |
| 28 #include "HTMLOptionElement.h" | 28 #include "HTMLOptionElement.h" |
| 29 | 29 |
| 30 #include "Attribute.h" | 30 #include "Attribute.h" |
| 31 #include "Document.h" | 31 #include "Document.h" |
| 32 #include "ExceptionCode.h" | 32 #include "ExceptionCode.h" |
| 33 #include "HTMLDataListElement.h" | 33 #include "HTMLDataListElement.h" |
| 34 #include "HTMLNames.h" | 34 #include "HTMLNames.h" |
| 35 #include "HTMLParserIdioms.h" | 35 #include "HTMLParserIdioms.h" |
| 36 #include "HTMLSelectElement.h" | 36 #include "HTMLSelectElement.h" |
| 37 #include "NodeRenderStyle.h" | |
| 38 #include "NodeRenderingContext.h" | |
| 39 #include "NodeTraversal.h" | 37 #include "NodeTraversal.h" |
| 40 #include "RenderMenuList.h" | 38 #include "RenderMenuList.h" |
| 41 #include "RenderTheme.h" | 39 #include "RenderTheme.h" |
| 42 #include "ScriptElement.h" | 40 #include "ScriptElement.h" |
| 41 #include "ShadowRoot.h" | |
| 43 #include "StyleResolver.h" | 42 #include "StyleResolver.h" |
| 44 #include "Text.h" | 43 #include "Text.h" |
| 45 #include <wtf/StdLibExtras.h> | 44 #include <wtf/StdLibExtras.h> |
| 46 #include <wtf/Vector.h> | 45 #include <wtf/Vector.h> |
| 47 #include <wtf/text/StringBuilder.h> | 46 #include <wtf/text/StringBuilder.h> |
| 48 | 47 |
| 49 namespace WebCore { | 48 namespace WebCore { |
| 50 | 49 |
| 51 using namespace HTMLNames; | 50 using namespace HTMLNames; |
| 52 | 51 |
| 53 HTMLOptionElement::HTMLOptionElement(const QualifiedName& tagName, Document* doc ument) | 52 HTMLOptionElement::HTMLOptionElement(const QualifiedName& tagName, Document* doc ument) |
| 54 : HTMLElement(tagName, document) | 53 : HTMLElement(tagName, document) |
| 55 , m_disabled(false) | 54 , m_disabled(false) |
| 56 , m_isSelected(false) | 55 , m_isSelected(false) |
| 57 { | 56 { |
| 58 ASSERT(hasTagName(optionTag)); | 57 ASSERT(hasTagName(optionTag)); |
| 59 setHasCustomStyleCallbacks(); | 58 setHasCustomStyleCallbacks(); |
| 60 ScriptWrappable::init(this); | 59 ScriptWrappable::init(this); |
| 61 } | 60 } |
| 62 | 61 |
| 63 PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(Document* document) | 62 PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(Document* document) |
| 64 { | 63 { |
| 65 return adoptRef(new HTMLOptionElement(optionTag, document)); | 64 return HTMLOptionElement::create(optionTag, document); |
| 66 } | 65 } |
| 67 | 66 |
| 68 PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(const QualifiedName& tag Name, Document* document) | 67 PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(const QualifiedName& tag Name, Document* document) |
| 69 { | 68 { |
| 70 return adoptRef(new HTMLOptionElement(tagName, document)); | 69 RefPtr<HTMLOptionElement> option = adoptRef(new HTMLOptionElement(tagName, d ocument)); |
| 70 option->ensureUserAgentShadowRoot(); | |
|
yosin_UTC9
2013/05/01 01:38:21
Can we postpone creating shadow root until this op
| |
| 71 return option.release(); | |
| 71 } | 72 } |
| 72 | 73 |
| 73 PassRefPtr<HTMLOptionElement> HTMLOptionElement::createForJSConstructor(Document * document, const String& data, const String& value, | 74 PassRefPtr<HTMLOptionElement> HTMLOptionElement::createForJSConstructor(Document * document, const String& data, const String& value, |
| 74 bool defaultSelected, bool selected, ExceptionCode& ec) | 75 bool defaultSelected, bool selected, ExceptionCode& ec) |
| 75 { | 76 { |
| 76 RefPtr<HTMLOptionElement> element = adoptRef(new HTMLOptionElement(optionTag , document)); | 77 RefPtr<HTMLOptionElement> element = HTMLOptionElement::create(optionTag, doc ument); |
| 77 | 78 |
| 78 RefPtr<Text> text = Text::create(document, data.isNull() ? "" : data); | 79 RefPtr<Text> text = Text::create(document, data.isNull() ? "" : data); |
| 79 | 80 |
| 80 ec = 0; | 81 ec = 0; |
| 81 element->appendChild(text.release(), ec); | 82 element->appendChild(text.release(), ec); |
| 82 if (ec) | 83 if (ec) |
| 83 return 0; | 84 return 0; |
| 84 | 85 |
| 85 if (!value.isNull()) | 86 if (!value.isNull()) |
| 86 element->setValue(value); | 87 element->setValue(value); |
| 87 if (defaultSelected) | 88 if (defaultSelected) |
| 88 element->setAttribute(selectedAttr, emptyAtom); | 89 element->setAttribute(selectedAttr, emptyAtom); |
| 89 element->setSelected(selected); | 90 element->setSelected(selected); |
| 90 | 91 |
| 91 return element.release(); | 92 return element.release(); |
| 92 } | 93 } |
| 93 | 94 |
| 94 void HTMLOptionElement::attach() | |
| 95 { | |
| 96 HTMLElement::attach(); | |
| 97 // If after attaching nothing called styleForRenderer() on this node we | |
| 98 // manually cache the value. This happens if our parent doesn't have a | |
| 99 // renderer like <optgroup> or if it doesn't allow children like <select>. | |
| 100 if (!m_style && parentNode()->renderStyle()) | |
| 101 updateNonRenderStyle(); | |
| 102 } | |
| 103 | |
| 104 void HTMLOptionElement::detach() | |
| 105 { | |
| 106 m_style.clear(); | |
| 107 HTMLElement::detach(); | |
| 108 } | |
| 109 | |
| 110 bool HTMLOptionElement::supportsFocus() const | 95 bool HTMLOptionElement::supportsFocus() const |
| 111 { | 96 { |
| 112 return HTMLElement::supportsFocus(); | 97 return HTMLElement::supportsFocus(); |
| 113 } | 98 } |
| 114 | 99 |
| 115 bool HTMLOptionElement::isFocusable() const | 100 bool HTMLOptionElement::isFocusable() const |
| 116 { | 101 { |
| 117 // Option elements do not have a renderer so we check the renderStyle instea d. | 102 // Option elements do not have a renderer so we check the renderStyle instea d. |
| 118 return supportsFocus() && renderStyle() && renderStyle()->display() != NONE; | 103 return supportsFocus() && renderStyle() && renderStyle()->display() != NONE; |
| 119 } | 104 } |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 209 renderer()->theme()->stateChanged(renderer(), EnabledState); | 194 renderer()->theme()->stateChanged(renderer(), EnabledState); |
| 210 } | 195 } |
| 211 } else if (name == selectedAttr) { | 196 } else if (name == selectedAttr) { |
| 212 // FIXME: This doesn't match what the HTML specification says. | 197 // FIXME: This doesn't match what the HTML specification says. |
| 213 // The specification implies that removing the selected attribute or | 198 // The specification implies that removing the selected attribute or |
| 214 // changing the value of a selected attribute that is already present | 199 // changing the value of a selected attribute that is already present |
| 215 // has no effect on whether the element is selected. Further, it seems | 200 // has no effect on whether the element is selected. Further, it seems |
| 216 // that we need to do more than just set m_isSelected to select in that | 201 // that we need to do more than just set m_isSelected to select in that |
| 217 // case; we'd need to do the other work from the setSelected function. | 202 // case; we'd need to do the other work from the setSelected function. |
| 218 m_isSelected = !value.isNull(); | 203 m_isSelected = !value.isNull(); |
| 204 } else if (name == labelAttr) { | |
| 205 updateLabel(); | |
| 219 } else | 206 } else |
| 220 HTMLElement::parseAttribute(name, value); | 207 HTMLElement::parseAttribute(name, value); |
| 221 } | 208 } |
| 222 | 209 |
| 223 String HTMLOptionElement::value() const | 210 String HTMLOptionElement::value() const |
| 224 { | 211 { |
| 225 const AtomicString& value = fastGetAttribute(valueAttr); | 212 const AtomicString& value = fastGetAttribute(valueAttr); |
| 226 if (!value.isNull()) | 213 if (!value.isNull()) |
| 227 return value; | 214 return value; |
| 228 return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSp ace(isHTMLSpace); | 215 return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSp ace(isHTMLSpace); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 | 252 |
| 266 void HTMLOptionElement::childrenChanged(bool changedByParser, Node* beforeChange , Node* afterChange, int childCountDelta) | 253 void HTMLOptionElement::childrenChanged(bool changedByParser, Node* beforeChange , Node* afterChange, int childCountDelta) |
| 267 { | 254 { |
| 268 #if ENABLE(DATALIST_ELEMENT) | 255 #if ENABLE(DATALIST_ELEMENT) |
| 269 if (HTMLDataListElement* dataList = ownerDataListElement()) | 256 if (HTMLDataListElement* dataList = ownerDataListElement()) |
| 270 dataList->optionElementChildrenChanged(); | 257 dataList->optionElementChildrenChanged(); |
| 271 else | 258 else |
| 272 #endif | 259 #endif |
| 273 if (HTMLSelectElement* select = ownerSelectElement()) | 260 if (HTMLSelectElement* select = ownerSelectElement()) |
| 274 select->optionElementChildrenChanged(); | 261 select->optionElementChildrenChanged(); |
| 262 updateLabel(); | |
| 275 HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, chi ldCountDelta); | 263 HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, chi ldCountDelta); |
| 276 } | 264 } |
| 277 | 265 |
| 278 #if ENABLE(DATALIST_ELEMENT) | 266 #if ENABLE(DATALIST_ELEMENT) |
| 279 HTMLDataListElement* HTMLOptionElement::ownerDataListElement() const | 267 HTMLDataListElement* HTMLOptionElement::ownerDataListElement() const |
| 280 { | 268 { |
| 281 for (ContainerNode* parent = parentNode(); parent ; parent = parent->parentN ode()) { | 269 for (ContainerNode* parent = parentNode(); parent ; parent = parent->parentN ode()) { |
| 282 if (parent->hasTagName(datalistTag)) | 270 if (parent->hasTagName(datalistTag)) |
| 283 return static_cast<HTMLDataListElement*>(parent); | 271 return static_cast<HTMLDataListElement*>(parent); |
| 284 } | 272 } |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 304 if (!label.isNull()) | 292 if (!label.isNull()) |
| 305 return label; | 293 return label; |
| 306 return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSp ace(isHTMLSpace); | 294 return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSp ace(isHTMLSpace); |
| 307 } | 295 } |
| 308 | 296 |
| 309 void HTMLOptionElement::setLabel(const String& label) | 297 void HTMLOptionElement::setLabel(const String& label) |
| 310 { | 298 { |
| 311 setAttribute(labelAttr, label); | 299 setAttribute(labelAttr, label); |
| 312 } | 300 } |
| 313 | 301 |
| 314 void HTMLOptionElement::updateNonRenderStyle() | |
| 315 { | |
| 316 m_style = document()->styleResolver()->styleForElement(this); | |
| 317 } | |
| 318 | |
| 319 RenderStyle* HTMLOptionElement::nonRendererStyle() const | |
| 320 { | |
| 321 return m_style.get(); | |
| 322 } | |
| 323 | |
| 324 PassRefPtr<RenderStyle> HTMLOptionElement::customStyleForRenderer() | |
| 325 { | |
| 326 // styleForRenderer is called whenever a new style should be associated | |
| 327 // with an Element so now is a good time to update our cached style. | |
| 328 updateNonRenderStyle(); | |
| 329 return m_style; | |
| 330 } | |
| 331 | |
| 332 void HTMLOptionElement::didRecalcStyle(StyleChange) | |
| 333 { | |
| 334 // FIXME: This is nasty, we ask our owner select to repaint even if the new | |
| 335 // style is exactly the same. | |
| 336 if (HTMLSelectElement* select = ownerSelectElement()) { | |
| 337 if (RenderObject* renderer = select->renderer()) | |
| 338 renderer->repaint(); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 String HTMLOptionElement::textIndentedToRespectGroupLabel() const | 302 String HTMLOptionElement::textIndentedToRespectGroupLabel() const |
| 343 { | 303 { |
| 344 ContainerNode* parent = parentNode(); | 304 ContainerNode* parent = parentNode(); |
| 345 if (parent && parent->hasTagName(optgroupTag)) | 305 if (parent && parent->hasTagName(optgroupTag)) |
| 346 return " " + text(); | 306 return " " + text(); |
| 347 return text(); | 307 return text(); |
| 348 } | 308 } |
| 349 | 309 |
| 350 bool HTMLOptionElement::isDisabledFormControl() const | 310 bool HTMLOptionElement::isDisabledFormControl() const |
| 351 { | 311 { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 383 text.append(node->nodeValue()); | 343 text.append(node->nodeValue()); |
| 384 // Text nodes inside script elements are not part of the option text. | 344 // Text nodes inside script elements are not part of the option text. |
| 385 if (node->isElementNode() && toScriptElementIfPossible(toElement(node))) | 345 if (node->isElementNode() && toScriptElementIfPossible(toElement(node))) |
| 386 node = NodeTraversal::nextSkippingChildren(node, this); | 346 node = NodeTraversal::nextSkippingChildren(node, this); |
| 387 else | 347 else |
| 388 node = NodeTraversal::next(node, this); | 348 node = NodeTraversal::next(node, this); |
| 389 } | 349 } |
| 390 return text.toString(); | 350 return text.toString(); |
| 391 } | 351 } |
| 392 | 352 |
| 353 void HTMLOptionElement::updateLabel() | |
| 354 { | |
| 355 Text* const textNode = toText(userAgentShadowRoot()->firstChild()); | |
| 356 if (textNode) | |
| 357 textNode->replaceWholeText(text(), ASSERT_NO_EXCEPTION); | |
| 358 else | |
| 359 userAgentShadowRoot()->appendChild(Text::create(document(), text()), ASS ERT_NO_EXCEPTION); | |
| 360 } | |
| 361 | |
| 362 PassRefPtr<RenderStyle> HTMLOptionElement::customStyleForRenderer() | |
| 363 { | |
| 364 RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForEle ment(this); | |
| 365 RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get()); | |
| 366 | |
| 367 HTMLSelectElement* selectElement = ownerSelectElement(); | |
| 368 if (selectElement) { | |
| 369 const Vector<HTMLElement*>& items = selectElement->listItems(); | |
| 370 if (items[items.size() - 1] == this) | |
|
yosin_UTC9
2013/05/01 01:38:21
nit: We should check item.size() > 0.
| |
| 371 style->setPaddingBottom(Length(0, Fixed)); | |
| 372 } | |
| 373 | |
| 374 return style.release(); | |
| 375 } | |
| 376 | |
| 393 #ifndef NDEBUG | 377 #ifndef NDEBUG |
| 394 | 378 |
| 395 HTMLOptionElement* toHTMLOptionElement(Node* node) | 379 HTMLOptionElement* toHTMLOptionElement(Node* node) |
| 396 { | 380 { |
| 397 ASSERT_WITH_SECURITY_IMPLICATION(!node || node->hasTagName(optionTag)); | 381 ASSERT_WITH_SECURITY_IMPLICATION(!node || node->hasTagName(optionTag)); |
| 398 return static_cast<HTMLOptionElement*>(node); | 382 return static_cast<HTMLOptionElement*>(node); |
| 399 } | 383 } |
| 400 | 384 |
| 401 const HTMLOptionElement* toHTMLOptionElement(const Node* node) | 385 const HTMLOptionElement* toHTMLOptionElement(const Node* node) |
| 402 { | 386 { |
| 403 ASSERT_WITH_SECURITY_IMPLICATION(!node || node->hasTagName(optionTag)); | 387 ASSERT_WITH_SECURITY_IMPLICATION(!node || node->hasTagName(optionTag)); |
| 404 return static_cast<const HTMLOptionElement*>(node); | 388 return static_cast<const HTMLOptionElement*>(node); |
| 405 } | 389 } |
| 406 | 390 |
| 407 #endif | 391 #endif |
| 408 | 392 |
| 409 } // namespace | 393 } // namespace |
| OLD | NEW |