Skip to content

Commit f5daf20

Browse files
committed
Firefox: fixing appending to contenteditable elements. Fixes issue 1419
1 parent cb6e9db commit f5daf20

File tree

4 files changed

+154
-104
lines changed

4 files changed

+154
-104
lines changed

common/src/web/readOnlyPage.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<textarea rows="5" id="textAreaNotenabled" disabled="true" cols="20">
1717
text area which is not supposed to be cleared</textarea>
1818

19-
<div id="content-editable" contentEditable="true">This is a contentEditable area</div>
19+
<div id="content-editable" contentEditable="true"><h1>This</h1><h2>is a</h2><p>contentEditable area</p></div>
2020

2121
<div id="content-editable-blank" contentEditable="true" style="height:50px;"></div>
2222
</body>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium;
19+
20+
import static org.hamcrest.Matchers.anyOf;
21+
import static org.hamcrest.Matchers.equalTo;
22+
import static org.junit.Assert.assertEquals;
23+
import static org.junit.Assert.assertThat;
24+
import static org.junit.Assume.assumeFalse;
25+
import static org.openqa.selenium.testing.Ignore.Driver.CHROME;
26+
import static org.openqa.selenium.testing.Ignore.Driver.HTMLUNIT;
27+
import static org.openqa.selenium.testing.Ignore.Driver.IE;
28+
import static org.openqa.selenium.testing.Ignore.Driver.MARIONETTE;
29+
import static org.openqa.selenium.testing.Ignore.Driver.SAFARI;
30+
import static org.openqa.selenium.testing.TestUtilities.getEffectivePlatform;
31+
import static org.openqa.selenium.testing.TestUtilities.isFirefox;
32+
33+
import org.junit.After;
34+
import org.junit.Test;
35+
import org.openqa.selenium.testing.Ignore;
36+
import org.openqa.selenium.testing.JUnit4TestBase;
37+
import org.openqa.selenium.testing.JavascriptEnabled;
38+
import org.openqa.selenium.testing.NotYetImplemented;
39+
40+
public class ContentEditableTest extends JUnit4TestBase {
41+
42+
@After
43+
public void switchToDefaultContent() {
44+
driver.switchTo().defaultContent();
45+
}
46+
47+
@JavascriptEnabled
48+
@Ignore(value = {SAFARI, MARIONETTE}, reason = "Safari: cannot type on contentEditable with synthetic events")
49+
@Test
50+
public void testTypingIntoAnIFrameWithContentEditableOrDesignModeSet() {
51+
driver.get(pages.richTextPage);
52+
53+
driver.switchTo().frame("editFrame");
54+
WebElement element = driver.switchTo().activeElement();
55+
element.sendKeys("Fishy");
56+
57+
driver.switchTo().defaultContent();
58+
WebElement trusted = driver.findElement(By.id("istrusted"));
59+
WebElement id = driver.findElement(By.id("tagId"));
60+
61+
assertThat(trusted.getText(), anyOf(
62+
equalTo("[true]"),
63+
// Chrome does not set a trusted flag.
64+
equalTo("[n/a]"),
65+
equalTo("[]")));
66+
assertThat(id.getText(), anyOf(equalTo("[frameHtml]"), equalTo("[theBody]")));
67+
}
68+
69+
@JavascriptEnabled
70+
@NotYetImplemented(HTMLUNIT)
71+
@Test
72+
@Ignore(MARIONETTE)
73+
public void testNonPrintableCharactersShouldWorkWithContentEditableOrDesignModeSet() {
74+
assumeFalse("FIXME: Fails in Firefox on Linux with synthesized events",
75+
isFirefox(driver) &&
76+
(getEffectivePlatform().is(Platform.LINUX) || getEffectivePlatform().is(Platform.MAC)));
77+
78+
driver.get(pages.richTextPage);
79+
80+
driver.switchTo().frame("editFrame");
81+
WebElement element = driver.switchTo().activeElement();
82+
element.sendKeys("Dishy", Keys.BACK_SPACE, Keys.LEFT, Keys.LEFT);
83+
element.sendKeys(Keys.LEFT, Keys.LEFT, "F", Keys.DELETE, Keys.END, "ee!");
84+
85+
assertEquals("Fishee!", element.getText());
86+
}
87+
88+
@Ignore(value = {SAFARI, HTMLUNIT}, reason = "Untested browsers;" +
89+
" Safari: cannot type on contentEditable with synthetic events",
90+
issues = {3127})
91+
@Test
92+
public void testShouldBeAbleToTypeIntoEmptyContentEditableElement() {
93+
driver.get(pages.readOnlyPage);
94+
WebElement editable = driver.findElement(By.id("content-editable-blank"));
95+
96+
editable.sendKeys("cheese");
97+
98+
assertThat(editable.getText(), equalTo("cheese"));
99+
}
100+
101+
@Ignore(value = {CHROME, IE, SAFARI, HTMLUNIT, MARIONETTE})
102+
@Test
103+
public void testShouldBeAbleToTypeIntoContentEditableElementWithExistingValue() {
104+
driver.get(pages.readOnlyPage);
105+
WebElement editable = driver.findElement(By.id("content-editable"));
106+
107+
String initialText = editable.getText();
108+
editable.sendKeys(", edited");
109+
110+
assertThat(editable.getText(), equalTo(initialText + ", edited"));
111+
}
112+
113+
@Ignore(value = {IE, SAFARI, HTMLUNIT, MARIONETTE},
114+
reason = "Untested browsers;" +
115+
" Safari: cannot type on contentEditable with synthetic events",
116+
issues = {3127})
117+
@Test
118+
public void testShouldBeAbleToTypeIntoTinyMCE() {
119+
driver.get(appServer.whereIs("tinymce.html"));
120+
driver.switchTo().frame("mce_0_ifr");
121+
122+
WebElement editable = driver.findElement(By.id("tinymce"));
123+
124+
editable.clear();
125+
editable.sendKeys("cheese"); // requires focus on OS X
126+
127+
assertThat(editable.getText(), equalTo("cheese"));
128+
}
129+
130+
@Ignore(value = {IE, SAFARI, HTMLUNIT, MARIONETTE},
131+
reason = "Untested browsers;" +
132+
" Safari: cannot type on contentEditable with synthetic events",
133+
issues = {3127})
134+
@Test
135+
public void testShouldAppendToTinyMCE() {
136+
driver.get(appServer.whereIs("tinymce.html"));
137+
driver.switchTo().frame("mce_0_ifr");
138+
139+
WebElement editable = driver.findElement(By.id("tinymce"));
140+
141+
editable.sendKeys(" and cheese"); // requires focus on OS X
142+
143+
assertThat(editable.getText(), equalTo("Initial content and cheese"));
144+
}
145+
146+
}

java/client/test/org/openqa/selenium/TypingTest.java

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
import static org.openqa.selenium.testing.TestUtilities.getEffectivePlatform;
3535
import static org.openqa.selenium.testing.TestUtilities.getFirefoxVersion;
3636
import static org.openqa.selenium.testing.TestUtilities.isFirefox;
37-
import static org.openqa.selenium.testing.TestUtilities.isInternetExplorer;
38-
import static org.openqa.selenium.testing.TestUtilities.getIEVersion;
3937

4038
import com.google.common.base.Joiner;
4139

@@ -647,49 +645,6 @@ public void testGenerateKeyPressEventEvenWhenElementPreventsDefault() {
647645
assertThat(result.getText().trim(), is(""));
648646
}
649647

650-
@JavascriptEnabled
651-
@Ignore(value = {SAFARI, MARIONETTE}, reason = "Safari: cannot type on contentEditable with synthetic events")
652-
@NoDriverAfterTest // So that next test never starts with "inside a frame" base state.
653-
@Test
654-
public void testTypingIntoAnIFrameWithContentEditableOrDesignModeSet() {
655-
driver.get(pages.richTextPage);
656-
657-
driver.switchTo().frame("editFrame");
658-
WebElement element = driver.switchTo().activeElement();
659-
element.sendKeys("Fishy");
660-
661-
driver.switchTo().defaultContent();
662-
WebElement trusted = driver.findElement(By.id("istrusted"));
663-
WebElement id = driver.findElement(By.id("tagId"));
664-
665-
assertThat(trusted.getText(), anyOf(
666-
equalTo("[true]"),
667-
// Chrome does not set a trusted flag.
668-
equalTo("[n/a]"),
669-
equalTo("[]")));
670-
assertThat(id.getText(), anyOf(equalTo("[frameHtml]"), equalTo("[theBody]")));
671-
}
672-
673-
@JavascriptEnabled
674-
@NotYetImplemented(HTMLUNIT)
675-
@NoDriverAfterTest // So that next test never starts with "inside a frame" base state.
676-
@Test
677-
@Ignore(MARIONETTE)
678-
public void testNonPrintableCharactersShouldWorkWithContentEditableOrDesignModeSet() {
679-
assumeFalse("FIXME: Fails in Firefox on Linux with synthesized events",
680-
isFirefox(driver) &&
681-
(getEffectivePlatform().is(Platform.LINUX) || getEffectivePlatform().is(Platform.MAC)));
682-
683-
driver.get(pages.richTextPage);
684-
685-
driver.switchTo().frame("editFrame");
686-
WebElement element = driver.switchTo().activeElement();
687-
element.sendKeys("Dishy", Keys.BACK_SPACE, Keys.LEFT, Keys.LEFT);
688-
element.sendKeys(Keys.LEFT, Keys.LEFT, "F", Keys.DELETE, Keys.END, "ee!");
689-
690-
assertEquals("Fishee!", element.getText());
691-
}
692-
693648
@JavascriptEnabled
694649
@Test
695650
public void testShouldBeAbleToTypeOnAnEmailInputField() {
@@ -708,49 +663,6 @@ public void testShouldBeAbleToTypeOnANumberInputField() {
708663
assertThat(email.getAttribute("value"), equalTo("33"));
709664
}
710665

711-
@Ignore(value = {SAFARI, HTMLUNIT}, reason = "Untested browsers;" +
712-
" Safari: cannot type on contentEditable with synthetic events",
713-
issues = {3127})
714-
@Test
715-
public void testShouldBeAbleToTypeIntoEmptyContentEditableElement() {
716-
driver.get(pages.readOnlyPage);
717-
WebElement editable = driver.findElement(By.id("content-editable-blank"));
718-
719-
editable.sendKeys("cheese");
720-
721-
assertThat(editable.getText(), equalTo("cheese"));
722-
}
723-
724-
@Ignore(value = {CHROME, IE, SAFARI, HTMLUNIT, MARIONETTE})
725-
@Test
726-
public void testShouldBeAbleToTypeIntoContentEditableElementWithExistingValue() {
727-
driver.get(pages.readOnlyPage);
728-
WebElement editable = driver.findElement(By.id("content-editable"));
729-
730-
String initialText = editable.getText();
731-
editable.sendKeys(", edited");
732-
733-
assertThat(editable.getText(), equalTo(initialText + ", edited"));
734-
}
735-
736-
@Ignore(value = {IE, SAFARI, HTMLUNIT, MARIONETTE},
737-
reason = "Untested browsers;" +
738-
" Safari: cannot type on contentEditable with synthetic events",
739-
issues = {3127})
740-
@NoDriverAfterTest // So that next test never starts with "inside a frame" base state.
741-
@Test
742-
public void testShouldBeAbleToTypeIntoTinyMCE() {
743-
driver.get(appServer.whereIs("tinymce.html"));
744-
driver.switchTo().frame("mce_0_ifr");
745-
746-
WebElement editable = driver.findElement(By.id("tinymce"));
747-
748-
editable.clear();
749-
editable.sendKeys("cheese"); // requires focus on OS X
750-
751-
assertThat(editable.getText(), equalTo("cheese"));
752-
}
753-
754666
@JavascriptEnabled
755667
@Ignore(value = {SAFARI}, reason = "Untested")
756668
@Test

javascript/firefox-driver/js/wrappedElement.js

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,23 +154,15 @@ WebElement.sendKeysToElement = function(respond, parameters) {
154154
var length = element.value ? element.value.length : goog.dom.getTextContent(element).length;
155155

156156
if (bot.dom.isContentEditable(element) && length) {
157+
var setCursorTo = element;
158+
if (element.lastElementChild) {
159+
setCursorTo = element.lastElementChild;
160+
}
161+
goog.log.info(WebElement.LOG_, 'ContentEditable ' + element + " " + length);
157162
var doc = element.ownerDocument || element.document;
158163
var rng = doc.createRange();
159-
var walker = doc.createTreeWalker(element, 4/*NodeFilter.SHOW_TEXT*/, null, null);
160-
var start = length;
161-
var end = length;
162-
var n,pos = 0;
163-
while (n = walker.nextNode()) {
164-
pos += n.nodeValue.length;
165-
if (pos >= start) {
166-
rng.setStart(n, n.nodeValue.length + start - pos);
167-
start = Infinity;
168-
}
169-
if (pos >= end) {
170-
rng.setEnd(n, n.nodeValue.length + end - pos);
171-
break;
172-
}
173-
}
164+
rng.selectNodeContents(setCursorTo);
165+
rng.collapse(false);
174166
var sel = doc.getSelection();
175167
sel.removeAllRanges();
176168
sel.addRange(rng);

0 commit comments

Comments
 (0)