Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLTextFormControlElementTest.cpp
index 1134854..26a7ad2 100644 (file)
@@ -5,13 +5,26 @@
 #include "config.h"
 #include "core/html/HTMLTextFormControlElement.h"
 
+#include "core/dom/Position.h"
+#include "core/dom/Text.h"
+#include "core/editing/FrameSelection.h"
+#include "core/editing/SpellChecker.h"
+#include "core/editing/VisibleSelection.h"
+#include "core/editing/VisibleUnits.h"
 #include "core/frame/FrameView.h"
+#include "core/html/HTMLBRElement.h"
 #include "core/html/HTMLDocument.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/HTMLTextAreaElement.h"
+#include "core/loader/EmptyClients.h"
+#include "core/page/SpellCheckerClient.h"
+#include "core/rendering/RenderTreeAsText.h"
 #include "core/testing/DummyPageHolder.h"
+#include "core/testing/UnitTestHelpers.h"
 #include "wtf/OwnPtr.h"
 #include <gtest/gtest.h>
 
-using namespace WebCore;
+using namespace blink;
 
 namespace {
 
@@ -19,22 +32,66 @@ class HTMLTextFormControlElementTest : public ::testing::Test {
 protected:
     virtual void SetUp() OVERRIDE;
 
+    DummyPageHolder& page() const { return *m_dummyPageHolder; }
+    HTMLDocument& document() const { return *m_document; }
     HTMLTextFormControlElement& textControl() const { return *m_textControl; }
+    HTMLInputElement& input() const { return *m_input; }
+
+    int layoutCount() const { return page().frameView().layoutCount(); }
+    void forceLayoutFlag();
 
 private:
+    OwnPtr<SpellCheckerClient> m_spellCheckerClient;
     OwnPtr<DummyPageHolder> m_dummyPageHolder;
 
+    RefPtrWillBePersistent<HTMLDocument> m_document;
     RefPtrWillBePersistent<HTMLTextFormControlElement> m_textControl;
+    RefPtrWillBePersistent<HTMLInputElement> m_input;
+};
+
+class DummyTextCheckerClient : public EmptyTextCheckerClient {
+public:
+    ~DummyTextCheckerClient() { }
+
+    virtual bool shouldEraseMarkersAfterChangeSelection(TextCheckingType) const OVERRIDE { return false; }
+};
+
+class DummySpellCheckerClient : public EmptySpellCheckerClient {
+public:
+    virtual ~DummySpellCheckerClient() { }
+
+    virtual bool isContinuousSpellCheckingEnabled() OVERRIDE { return true; }
+    virtual bool isGrammarCheckingEnabled() OVERRIDE { return true; }
+
+    virtual TextCheckerClient& textChecker() OVERRIDE { return m_dummyTextCheckerClient; }
+
+private:
+    DummyTextCheckerClient m_dummyTextCheckerClient;
 };
 
 void HTMLTextFormControlElementTest::SetUp()
 {
-    m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
-    HTMLDocument& document = toHTMLDocument(m_dummyPageHolder->document());
-    document.documentElement()->setInnerHTML("<body><textarea id=textarea></textarea></body>", ASSERT_NO_EXCEPTION);
-    document.view()->updateLayoutAndStyleIfNeededRecursive();
-    m_textControl = toHTMLTextFormControlElement(document.getElementById("textarea"));
+    Page::PageClients pageClients;
+    fillWithEmptyClients(pageClients);
+    m_spellCheckerClient = adoptPtr(new DummySpellCheckerClient);
+    pageClients.spellCheckerClient = m_spellCheckerClient.get();
+    m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600), &pageClients);
+
+    m_document = toHTMLDocument(&m_dummyPageHolder->document());
+    m_document->documentElement()->setInnerHTML("<body><textarea id=textarea></textarea><input id=input /></body>", ASSERT_NO_EXCEPTION);
+    m_document->view()->updateLayoutAndStyleIfNeededRecursive();
+    m_textControl = toHTMLTextFormControlElement(m_document->getElementById("textarea"));
     m_textControl->focus();
+    m_input = toHTMLInputElement(m_document->getElementById("input"));
+}
+
+void HTMLTextFormControlElementTest::forceLayoutFlag()
+{
+    FrameView& frameView = page().frameView();
+    IntRect frameRect = frameView.frameRect();
+    frameRect.setWidth(frameRect.width() + 1);
+    frameRect.setHeight(frameRect.height() + 1);
+    page().frameView().setFrameRect(frameRect);
 }
 
 TEST_F(HTMLTextFormControlElementTest, SetSelectionRange)
@@ -51,4 +108,121 @@ TEST_F(HTMLTextFormControlElementTest, SetSelectionRange)
     EXPECT_EQ(3, textControl().selectionEnd());
 }
 
+TEST_F(HTMLTextFormControlElementTest, SetSelectionRangeDoesNotCauseLayout)
+{
+    input().focus();
+    input().setValue("Hello, input form.");
+    input().setSelectionRange(1, 1);
+    FrameSelection& frameSelection = document().frame()->selection();
+    forceLayoutFlag();
+    LayoutRect oldCaretRect = frameSelection.absoluteCaretBounds();
+    EXPECT_FALSE(oldCaretRect.isEmpty());
+    int startLayoutCount = layoutCount();
+    input().setSelectionRange(1, 1);
+    EXPECT_EQ(startLayoutCount, layoutCount());
+    LayoutRect newCaretRect = frameSelection.absoluteCaretBounds();
+    EXPECT_EQ(oldCaretRect, newCaretRect);
+
+    forceLayoutFlag();
+    oldCaretRect = frameSelection.absoluteCaretBounds();
+    EXPECT_FALSE(oldCaretRect.isEmpty());
+    startLayoutCount = layoutCount();
+    input().setSelectionRange(2, 2);
+    EXPECT_EQ(startLayoutCount, layoutCount());
+    newCaretRect = frameSelection.absoluteCaretBounds();
+    EXPECT_NE(oldCaretRect, newCaretRect);
+}
+
+typedef Position (*PositionFunction)(const Position&);
+typedef VisiblePosition(*VisblePositionFunction)(const VisiblePosition&);
+
+void testFunctionEquivalence(const Position& position, PositionFunction positionFunction, VisblePositionFunction visibleFunction)
+{
+    VisiblePosition visiblePosition(position);
+    VisiblePosition expected = visibleFunction(visiblePosition);
+    VisiblePosition actual = VisiblePosition(positionFunction(position));
+    EXPECT_EQ(expected, actual);
+}
+
+static VisiblePosition startOfWord(const VisiblePosition& position)
+{
+    return startOfWord(position, LeftWordIfOnBoundary);
+}
+
+static VisiblePosition endOfWord(const VisiblePosition& position)
+{
+    return endOfWord(position, RightWordIfOnBoundary);
+}
+
+void testBoundary(HTMLDocument& document, HTMLTextFormControlElement& textControl)
+{
+    for (unsigned i = 0; i < textControl.innerEditorValue().length(); i++) {
+        textControl.setSelectionRange(i, i);
+        Position position = document.frame()->selection().start();
+        SCOPED_TRACE(::testing::Message() << "offset " << position.deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(position.deprecatedNode()).ascii().data());
+        {
+            SCOPED_TRACE("HTMLTextFormControlElement::startOfWord");
+            testFunctionEquivalence(position, HTMLTextFormControlElement::startOfWord, startOfWord);
+        }
+        {
+            SCOPED_TRACE("HTMLTextFormControlElement::endOfWord");
+            testFunctionEquivalence(position, HTMLTextFormControlElement::endOfWord, endOfWord);
+        }
+        {
+            SCOPED_TRACE("HTMLTextFormControlElement::startOfSentence");
+            testFunctionEquivalence(position, HTMLTextFormControlElement::startOfSentence, startOfSentence);
+        }
+        {
+            SCOPED_TRACE("HTMLTextFormControlElement::endOfSentence");
+            testFunctionEquivalence(position, HTMLTextFormControlElement::endOfSentence, endOfSentence);
+        }
+    }
+}
+
+TEST_F(HTMLTextFormControlElementTest, WordAndSentenceBoundary)
+{
+    HTMLElement* innerText = textControl().innerEditorElement();
+    {
+        SCOPED_TRACE("String is value.");
+        innerText->removeChildren();
+        innerText->setNodeValue("Hel\nlo, text form.\n");
+        testBoundary(document(), textControl());
+    }
+    {
+        SCOPED_TRACE("A Text node and a BR element");
+        innerText->removeChildren();
+        innerText->setNodeValue("");
+        innerText->appendChild(Text::create(document(), "Hello, text form."));
+        innerText->appendChild(HTMLBRElement::create(document()));
+        testBoundary(document(), textControl());
+    }
+    {
+        SCOPED_TRACE("Text nodes.");
+        innerText->removeChildren();
+        innerText->setNodeValue("");
+        innerText->appendChild(Text::create(document(), "Hel\nlo, te"));
+        innerText->appendChild(Text::create(document(), "xt form."));
+        testBoundary(document(), textControl());
+    }
+}
+
+TEST_F(HTMLTextFormControlElementTest, SpellCheckDoesNotCauseUpdateLayout)
+{
+    HTMLInputElement* input = toHTMLInputElement(document().getElementById("input"));
+    input->focus();
+    input->setValue("Hello, input field");
+    VisibleSelection oldSelection = document().frame()->selection().selection();
+
+    Position newPosition(input->innerEditorElement()->firstChild(), 3, Position::PositionIsOffsetInAnchor);
+    VisibleSelection newSelection(newPosition, DOWNSTREAM);
+    document().frame()->selection().setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::DoNotUpdateAppearance);
+    ASSERT_EQ(3, input->selectionStart());
+
+    OwnPtr<SpellChecker> spellChecker(SpellChecker::create(page().frame()));
+    forceLayoutFlag();
+    int startCount = layoutCount();
+    spellChecker->respondToChangedSelection(oldSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
+    EXPECT_EQ(startCount, layoutCount());
+}
+
 }