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 |