Diff
Modified: trunk/LayoutTests/ChangeLog (87979 => 87980)
--- trunk/LayoutTests/ChangeLog 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/LayoutTests/ChangeLog 2011-06-03 01:15:25 UTC (rev 87980)
@@ -1,3 +1,13 @@
+2011-06-02 Kent Tamura <tk...@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ REGRESSION(r87014): Cloned text <input> doesn't work well.
+ https://bugs.webkit.org/show_bug.cgi?id=61909
+
+ * fast/forms/textfield-clone-expected.txt: Added.
+ * fast/forms/textfield-clone.html: Added.
+
2011-06-02 Adam Barth <aba...@webkit.org>
Tweaks to which kind of failure we expect.
Added: trunk/LayoutTests/fast/forms/textfield-clone-expected.txt (0 => 87980)
--- trunk/LayoutTests/fast/forms/textfield-clone-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/forms/textfield-clone-expected.txt 2011-06-03 01:15:25 UTC (rev 87980)
@@ -0,0 +1,14 @@
+There was a bug that the value property of a cloned text input was not updated.
+
+Check for text type:
+PASS clone.value is newValue
+Check for search type:
+PASS clone.value is newValue
+Check for search type:
+PASS clone.value is newValue
+Check for number type:
+PASS clone.value is newValue
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/fast/forms/textfield-clone-expected.txt
___________________________________________________________________
Added: svn:eol-style
Added: trunk/LayoutTests/fast/forms/textfield-clone.html (0 => 87980)
--- trunk/LayoutTests/fast/forms/textfield-clone.html (rev 0)
+++ trunk/LayoutTests/fast/forms/textfield-clone.html 2011-06-03 01:15:25 UTC (rev 87980)
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src=""
+<p>There was a bug that the value property of a cloned text input was not updated.</p>
+<div id=container>
+<input id=i1 value="Initial" style>
+<input id=i2 type=search value="Initial" style>
+<!-- ENABLE_INPUT_SPEECH flag doesn't affect the test result -->
+<input id=i3 type=search value="Initial" x-webkit-speech style>
+<input id=i4 type=number value="0" style>
+</div>
+<div id=console></div>
+<script>
+var clone;
+var container = document.getElementById('container');
+var newValue;
+
+function check(id, value) {
+ var original = document.getElementById(id);
+ clone = original.cloneNode();
+ container.replaceChild(clone, original);
+ clone.focus();
+ document.execCommand('selectAll');
+ document.execCommand('insertText', false, value);
+ newValue = value;
+ debug('Check for ' + clone.type + ' type:');
+ shouldBe('clone.value', 'newValue');
+}
+
+check('i1', 'foo');
+check('i2', 'query');
+check('i3', 'speech');
+check('i4', '13');
+container.innerHTML = '';
+
+var successfullyParsed = true;
+</script>
+<script src=""
Property changes on: trunk/LayoutTests/fast/forms/textfield-clone.html
___________________________________________________________________
Added: svn:eol-style
Modified: trunk/Source/WebCore/ChangeLog (87979 => 87980)
--- trunk/Source/WebCore/ChangeLog 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/ChangeLog 2011-06-03 01:15:25 UTC (rev 87980)
@@ -1,3 +1,58 @@
+2011-06-02 Kent Tamura <tk...@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ REGRESSION(r87014): Cloned text <input> doesn't work well.
+ https://bugs.webkit.org/show_bug.cgi?id=61909
+
+ The default implementation of Element::cloneNode() creates a clone
+ node by Document::createElement(). So, if a customized class is used for
+ shadow nodes, cloneNode() doesn't create a node of the correct class.
+ Such custom classes need to override Element::
+ cloneElementWithoutAttributesAndChildren().
+
+ TextFieldInputType and SearchInputType had references to shadow nodes
+ initialized in createShadowSubtree(). However createShadowSubtree() is
+ not called if the host node is cloned. The accessors for shadow nodes
+ should search a shadow tree.
+
+ Test: fast/forms/textfield-clone.html
+
+ * html/SearchInputType.cpp:
+ Remove data members for shadow nodes, and the accessors search the
+ shadow tree for the requested node by TreeScope::getElementById().
+ (WebCore::SearchInputType::SearchInputType):
+ (WebCore::innerBlockId):
+ (WebCore::resultButtonId):
+ (WebCore::cancelButtonId):
+ (WebCore::SearchInputType::createShadowSubtree):
+ (WebCore::SearchInputType::innerBlockElement):
+ (WebCore::SearchInputType::resultsButtonElement):
+ (WebCore::SearchInputType::cancelButtonElement):
+ * html/SearchInputType.h:
+ * html/TextFieldInputType.cpp: ditto.
+ (WebCore::TextFieldInputType::TextFieldInputType):
+ (WebCore::TextFieldInputType::innerTextId):
+ (WebCore::spinButtonId):
+ (WebCore::TextFieldInputType::speechButtonId):
+ (WebCore::TextFieldInputType::appendChildAndSetId):
+ (WebCore::TextFieldInputType::createShadowSubtree):
+ (WebCore::TextFieldInputType::getShadowElementById):
+ (WebCore::TextFieldInputType::innerTextElement):
+ (WebCore::TextFieldInputType::innerSpinButtonElement):
+ (WebCore::TextFieldInputType::speechButtonElement):
+ * html/TextFieldInputType.h:
+ * html/shadow/TextControlInnerElements.cpp:
+ Overrides cloneElementWithoutAttributesAndChildren() in order that cloneNode()
+ produces an instance of the correct class.
+ (WebCore::TextControlInnerElement::cloneElementWithoutAttributesAndChildren):
+ (WebCore::TextControlInnerTextElement::cloneElementWithoutAttributesAndChildren):
+ (WebCore::SearchFieldResultsButtonElement::cloneElementWithoutAttributesAndChildren):
+ (WebCore::SearchFieldCancelButtonElement::cloneElementWithoutAttributesAndChildren):
+ (WebCore::SpinButtonElement::cloneElementWithoutAttributesAndChildren):
+ (WebCore::InputFieldSpeechButtonElement::cloneElementWithoutAttributesAndChildren):
+ * html/shadow/TextControlInnerElements.h:
+
2011-05-28 Martin Robinson <mrobin...@igalia.com>
Reviewed by Gustavo Noronha Silva.
Modified: trunk/Source/WebCore/html/SearchInputType.cpp (87979 => 87980)
--- trunk/Source/WebCore/html/SearchInputType.cpp 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/SearchInputType.cpp 2011-06-03 01:15:25 UTC (rev 87980)
@@ -40,9 +40,6 @@
inline SearchInputType::SearchInputType(HTMLInputElement* element)
: BaseTextInputType(element)
- , m_innerBlock(0)
- , m_resultsButton(0)
- , m_cancelButton(0)
{
}
@@ -66,47 +63,54 @@
return true;
}
+static const AtomicString& innerBlockId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("innerBlock"));
+ return id;
+}
+
+static const AtomicString& resultButtonId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("resultButton"));
+ return id;
+}
+
+static const AtomicString& cancelButtonId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("cancelButton"));
+ return id;
+}
+
void SearchInputType::createShadowSubtree()
{
- ASSERT(!m_innerBlock);
- ASSERT(!innerTextElement());
- ASSERT(!m_resultsButton);
- ASSERT(!m_cancelButton);
-
- ExceptionCode ec = 0;
Document* document = element()->document();
RefPtr<HTMLElement> inner = TextControlInnerElement::create(document);
- m_innerBlock = inner.get();
- element()->ensureShadowRoot()->appendChild(inner.release(), ec);
+ HTMLElement* innerBlock = inner.get();
+ appendChildAndSetId(element()->ensureShadowRoot(), inner.release(), innerBlockId());
#if ENABLE(INPUT_SPEECH)
- if (element()->isSpeechEnabled()) {
- RefPtr<HTMLElement> speech = InputFieldSpeechButtonElement::create(document);
- setSpeechButtonElement(speech.get());
- element()->ensureShadowRoot()->appendChild(speech.release(), ec);
- }
+ if (element()->isSpeechEnabled())
+ appendChildAndSetId(element()->ensureShadowRoot(), InputFieldSpeechButtonElement::create(document), speechButtonId());
#endif
- RefPtr<HTMLElement> results = SearchFieldResultsButtonElement::create(document);
- m_resultsButton = results.get();
- m_innerBlock->appendChild(results.release(), ec);
+ appendChildAndSetId(innerBlock, SearchFieldResultsButtonElement::create(document), resultButtonId());
+ appendChildAndSetId(innerBlock, TextControlInnerTextElement::create(document), innerTextId());
+ appendChildAndSetId(innerBlock, SearchFieldCancelButtonElement::create(document), cancelButtonId());
+}
- RefPtr<HTMLElement> innerText = TextControlInnerTextElement::create(document);
- setInnerTextElement(innerText.get());
- m_innerBlock->appendChild(innerText.release(), ec);
+HTMLElement* SearchInputType::innerBlockElement() const
+{
+ return getShadowElementById(innerBlockId());
+}
- RefPtr<HTMLElement> cancel = SearchFieldCancelButtonElement::create(element()->document());
- m_cancelButton = cancel.get();
- m_innerBlock->appendChild(cancel.release(), ec);
+HTMLElement* SearchInputType::resultsButtonElement() const
+{
+ return getShadowElementById(resultButtonId());
}
-void SearchInputType::destroyShadowSubtree()
+HTMLElement* SearchInputType::cancelButtonElement() const
{
- TextFieldInputType::destroyShadowSubtree();
- m_innerBlock = 0;
- m_resultsButton = 0;
- m_cancelButton = 0;
+ return getShadowElementById(cancelButtonId());
}
-
} // namespace WebCore
Modified: trunk/Source/WebCore/html/SearchInputType.h (87979 => 87980)
--- trunk/Source/WebCore/html/SearchInputType.h 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/SearchInputType.h 2011-06-03 01:15:25 UTC (rev 87980)
@@ -35,29 +35,19 @@
namespace WebCore {
-class SearchFieldCancelButtonElement;
-class SearchFieldResultsButtonElement;
-
class SearchInputType : public BaseTextInputType {
public:
static PassOwnPtr<InputType> create(HTMLInputElement*);
-protected:
- virtual void createShadowSubtree();
- virtual void destroyShadowSubtree();
-
private:
SearchInputType(HTMLInputElement*);
virtual const AtomicString& formControlType() const;
virtual bool shouldRespectSpeechAttribute();
virtual bool isSearchField() const;
- virtual HTMLElement* innerBlockElement() const { return m_innerBlock; }
- virtual HTMLElement* resultsButtonElement() const { return m_resultsButton; }
- virtual HTMLElement* cancelButtonElement() const { return m_cancelButton; }
-
- HTMLElement* m_innerBlock;
- HTMLElement* m_resultsButton;
- HTMLElement* m_cancelButton;
+ virtual void createShadowSubtree();
+ virtual HTMLElement* innerBlockElement() const;
+ virtual HTMLElement* resultsButtonElement() const;
+ virtual HTMLElement* cancelButtonElement() const;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/TextFieldInputType.cpp (87979 => 87980)
--- trunk/Source/WebCore/html/TextFieldInputType.cpp 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/TextFieldInputType.cpp 2011-06-03 01:15:25 UTC (rev 87980)
@@ -35,6 +35,7 @@
#include "BeforeTextInsertedEvent.h"
#include "Frame.h"
#include "HTMLInputElement.h"
+#include "HTMLNames.h"
#include "KeyboardEvent.h"
#include "RenderTextControlSingleLine.h"
#include "RenderTheme.h"
@@ -47,13 +48,10 @@
namespace WebCore {
+using namespace HTMLNames;
+
TextFieldInputType::TextFieldInputType(HTMLInputElement* element)
: InputType(element)
- , m_innerText(0)
- , m_innerSpinButton(0)
-#if ENABLE(INPUT_SPEECH)
- , m_speechButton(0)
-#endif
{
}
@@ -129,51 +127,81 @@
return new (arena) RenderTextControlSingleLine(element(), element()->placeholderShouldBeVisible());
}
+const AtomicString& TextFieldInputType::innerTextId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("innerText"));
+ return id;
+}
+
+static const AtomicString& spinButtonId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("spinButton"));
+ return id;
+}
+
+#if ENABLE(INPUT_SPEECH)
+const AtomicString& TextFieldInputType::speechButtonId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("speechButton"));
+ return id;
+}
+#endif
+
+void TextFieldInputType::appendChildAndSetId(ContainerNode* parent, PassRefPtr<HTMLElement> child, const AtomicString& id)
+{
+ ExceptionCode ec = 0;
+ child->setAttribute(idAttr, id);
+ parent->appendChild(child, ec);
+}
+
void TextFieldInputType::createShadowSubtree()
{
- ASSERT(!m_innerText);
- ASSERT(!m_innerSpinButton);
-
- bool shouldHaveSpinButton = RenderTheme::themeForPage(element()->document()->page())->shouldHaveSpinButton(element());
+ Document* document = element()->document();
+ bool shouldHaveSpinButton = RenderTheme::themeForPage(document->page())->shouldHaveSpinButton(element());
bool hasDecorations = shouldHaveSpinButton;
#if ENABLE(INPUT_SPEECH)
if (element()->isSpeechEnabled())
hasDecorations = true;
#endif
- ExceptionCode ec = 0;
- Document* document = element()->document();
- RefPtr<HTMLElement> innerText = TextControlInnerTextElement::create(document);
- m_innerText = innerText.get();
- element()->ensureShadowRoot()->appendChild(innerText.release(), ec);
+ ShadowRoot* shadowRoot = element()->ensureShadowRoot();
+ appendChildAndSetId(shadowRoot, TextControlInnerTextElement::create(document), innerTextId());
if (!hasDecorations)
return;
#if ENABLE(INPUT_SPEECH)
- ASSERT(!m_speechButton);
- if (element()->isSpeechEnabled()) {
- RefPtr<HTMLElement> speech = InputFieldSpeechButtonElement::create(document);
- m_speechButton = speech.get();
- element()->ensureShadowRoot()->appendChild(speech.release(), ec);
- }
+ if (element()->isSpeechEnabled())
+ appendChildAndSetId(shadowRoot, InputFieldSpeechButtonElement::create(document), speechButtonId());
#endif
- if (shouldHaveSpinButton) {
- RefPtr<HTMLElement> inner = SpinButtonElement::create(document);
- m_innerSpinButton = inner.get();
- element()->ensureShadowRoot()->appendChild(inner.release(), ec);
- }
+ if (shouldHaveSpinButton)
+ appendChildAndSetId(shadowRoot, SpinButtonElement::create(document), spinButtonId());
}
-void TextFieldInputType::destroyShadowSubtree()
+HTMLElement* TextFieldInputType::getShadowElementById(const AtomicString& id) const
{
- InputType::destroyShadowSubtree();
- m_innerText = 0;
+ if (!element()->shadowRoot())
+ return 0;
+ Element* shadow = element()->shadowRoot()->getElementById(id);
+ return shadow ? static_cast<HTMLElement*>(shadow) : 0;
+}
+
+HTMLElement* TextFieldInputType::innerTextElement() const
+{
+ return getShadowElementById(innerTextId());
+}
+
+HTMLElement* TextFieldInputType::innerSpinButtonElement() const
+{
+ return getShadowElementById(spinButtonId());
+}
+
#if ENABLE(INPUT_SPEECH)
- m_speechButton = 0;
+HTMLElement* TextFieldInputType::speechButtonElement() const
+{
+ return getShadowElementById(speechButtonId());
+}
#endif
- m_innerSpinButton = 0;
-}
bool TextFieldInputType::shouldUseInputMethod() const
{
Modified: trunk/Source/WebCore/html/TextFieldInputType.h (87979 => 87980)
--- trunk/Source/WebCore/html/TextFieldInputType.h 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/TextFieldInputType.h 2011-06-03 01:15:25 UTC (rev 87980)
@@ -35,6 +35,8 @@
namespace WebCore {
+class ContainerNode;
+
// The class represents types of which UI contain text fields.
// It supports not only the types for BaseTextInputType but also type=number.
class TextFieldInputType : public InputType {
@@ -45,19 +47,20 @@
void handleKeydownEventForSpinButton(KeyboardEvent*);
void handleWheelEventForSpinButton(WheelEvent*);
- virtual HTMLElement* innerTextElement() const { return m_innerText; }
- virtual HTMLElement* innerSpinButtonElement() const { return m_innerSpinButton; }
+ virtual HTMLElement* innerTextElement() const;
+ virtual HTMLElement* innerSpinButtonElement() const;
#if ENABLE(INPUT_SPEECH)
- virtual HTMLElement* speechButtonElement() const { return m_speechButton; }
+ virtual HTMLElement* speechButtonElement() const;
#endif
protected:
- virtual void createShadowSubtree();
- virtual void destroyShadowSubtree();
- void setInnerTextElement(HTMLElement* element) { m_innerText = element; }
+ const AtomicString& innerTextId() const;
#if ENABLE(INPUT_SPEECH)
- void setSpeechButtonElement(HTMLElement* element) { m_speechButton = element; }
+ const AtomicString& speechButtonId() const;
#endif
+ static void appendChildAndSetId(ContainerNode*, PassRefPtr<HTMLElement>, const AtomicString&);
+ virtual void createShadowSubtree();
+ HTMLElement* getShadowElementById(const AtomicString&) const;
private:
virtual bool isTextField() const;
@@ -69,12 +72,6 @@
virtual bool shouldUseInputMethod() const;
virtual String sanitizeValue(const String&);
virtual bool shouldRespectListAttribute();
-
- HTMLElement* m_innerText;
- HTMLElement* m_innerSpinButton;
-#if ENABLE(INPUT_SPEECH)
- HTMLElement* m_speechButton;
-#endif
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/shadow/TextControlInnerElements.cpp (87979 => 87980)
--- trunk/Source/WebCore/html/shadow/TextControlInnerElements.cpp 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/shadow/TextControlInnerElements.cpp 2011-06-03 01:15:25 UTC (rev 87980)
@@ -63,6 +63,11 @@
return parentRenderer->createInnerBlockStyle(parentRenderer->style());
}
+PassRefPtr<Element> TextControlInnerElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
+
// ----------------------------
inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document)
@@ -109,6 +114,11 @@
return parentRenderer->createInnerTextStyle(parentRenderer->style());
}
+PassRefPtr<Element> TextControlInnerTextElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
+
// ----------------------------
inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
@@ -158,6 +168,11 @@
HTMLDivElement::defaultEventHandler(event);
}
+PassRefPtr<Element> SearchFieldResultsButtonElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
+
// ----------------------------
inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
@@ -221,6 +236,11 @@
HTMLDivElement::defaultEventHandler(event);
}
+PassRefPtr<Element> SearchFieldCancelButtonElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
+
// ----------------------------
inline SpinButtonElement::SpinButtonElement(Document* document)
@@ -357,6 +377,10 @@
HTMLDivElement::setHovered(flag);
}
+PassRefPtr<Element> SpinButtonElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
// ----------------------------
@@ -525,6 +549,11 @@
return pseudoId;
}
+PassRefPtr<Element> InputFieldSpeechButtonElement::cloneElementWithoutAttributesAndChildren() const
+{
+ return create(document());
+}
+
#endif // ENABLE(INPUT_SPEECH)
}
Modified: trunk/Source/WebCore/html/shadow/TextControlInnerElements.h (87979 => 87980)
--- trunk/Source/WebCore/html/shadow/TextControlInnerElements.h 2011-06-03 01:13:44 UTC (rev 87979)
+++ trunk/Source/WebCore/html/shadow/TextControlInnerElements.h 2011-06-03 01:15:25 UTC (rev 87980)
@@ -46,6 +46,7 @@
private:
virtual bool isMouseFocusable() const { return false; }
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
};
class TextControlInnerTextElement : public HTMLDivElement {
@@ -59,6 +60,7 @@
virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
virtual PassRefPtr<RenderStyle> styleForRenderer();
virtual bool isMouseFocusable() const { return false; }
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
};
class SearchFieldResultsButtonElement : public HTMLDivElement {
@@ -71,6 +73,7 @@
SearchFieldResultsButtonElement(Document*);
virtual const AtomicString& shadowPseudoId() const;
virtual bool isMouseFocusable() const { return false; }
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
};
class SearchFieldCancelButtonElement : public HTMLDivElement {
@@ -84,6 +87,7 @@
virtual const AtomicString& shadowPseudoId() const;
virtual void detach();
virtual bool isMouseFocusable() const { return false; }
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
bool m_capturing;
};
@@ -113,6 +117,7 @@
void repeatingTimerFired(Timer<SpinButtonElement>*);
virtual void setHovered(bool = true);
virtual bool isMouseFocusable() const { return false; }
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
bool m_capturing;
UpDownState m_upDownState;
@@ -152,6 +157,7 @@
virtual const AtomicString& shadowPseudoId() const;
virtual bool isMouseFocusable() const { return false; }
virtual void attach();
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const;
bool m_capturing;
SpeechInputState m_state;