Bug 42767 - [Windows] Home hey doesn't work in first DIV inside a TABLE
Summary: [Windows] Home hey doesn't work in first DIV inside a TABLE
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: HTML Editing (show other bugs)
Version: 528+ (Nightly build)
Hardware: PC Windows Vista
: P2 Normal
Assignee: Nobody
URL:
Keywords: HasReduction
Depends on:
Blocks:
 
Reported: 2010-07-21 10:12 PDT by Julie Parent
Modified: 2010-07-22 16:21 PDT (History)
4 users (show)

See Also:


Attachments
Test case (201 bytes, text/html)
2010-07-21 10:12 PDT, Julie Parent
no flags Details
demonstrates the bug on mac using modify(move, backward, line-boundary) (344 bytes, text/html)
2010-07-21 10:48 PDT, Ryosuke Niwa
no flags Details
fixes the bug (6.30 KB, patch)
2010-07-21 21:48 PDT, Ryosuke Niwa
ojan: review-
ojan: commit-queue-
Details | Formatted Diff | Diff
fixed per Ojan's comment (9.12 KB, patch)
2010-07-22 11:32 PDT, Ryosuke Niwa
ojan: review-
ojan: commit-queue-
Details | Formatted Diff | Diff
fixed per ojan's comments (7.36 KB, patch)
2010-07-22 14:26 PDT, Ryosuke Niwa
ojan: review-
Details | Formatted Diff | Diff
fixed per ojan's comments (7.08 KB, patch)
2010-07-22 14:56 PDT, Ryosuke Niwa
ojan: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Julie Parent 2010-07-21 10:12:50 PDT
Created attachment 62202 [details]
Test case

Repro steps:
1. Create a page with the following HTML (or use attached test case):
<TABLE>
  <TR>
    <TD>
      <DIV contenteditable="true">
        <DIV>Line 1 - home does not work</DIV>
        <DIV>Line 2 - home does work</DIV>
      </DIV>
    </TD>
  </TR>
</TABLE>
2. Click into the contenteditable div and place the cursor inside Line 1.
3. Press Home

Expected result:
The cursor moves to the beginning of the line.

Result:
The cursor doesn't move.

Other notes:
* Happens in both Chromium on Windows and Safari on Windows.
* Works as expected on Line 2.
* Effects Google Sites (first line of any Sites page exhibits this bug).

(Related Chromium bug - crbug.com/49780)
Comment 1 Julie Parent 2010-07-21 10:21:56 PDT
Note that this is Windows only - Mac behavior is correct to not move the cursor when home key is used.
Comment 2 Ryosuke Niwa 2010-07-21 10:23:35 PDT
I guess Home/End doesn't move the caret in the line so this bug doesn't reproduce on Mac.
Comment 3 Ryosuke Niwa 2010-07-21 10:48:43 PDT
Created attachment 62211 [details]
demonstrates the bug on mac using modify(move, backward, line-boundary)
Comment 4 Ryosuke Niwa 2010-07-21 17:05:13 PDT
The problem lies in http://trac.webkit.org/browser/trunk/WebCore/editing/visible_units.cpp#L376 which is called by logicalStartPositionForLine < logicalStartOfLine < SelectionController::modifyMovingBackward.  Here, we are avoiding the first position in the table even if the previous position wasn't editable.  One line fix to add isEditablePosition(previous.deepEquivalent()) will fix the issue.
Comment 5 Ryosuke Niwa 2010-07-21 21:48:27 PDT
Created attachment 62262 [details]
fixes the bug
Comment 6 Ojan Vafai 2010-07-22 10:44:36 PDT
Comment on attachment 62262 [details]
fixes the bug

It would be nice if, in the cases where there are multiple dump calls, if each one could be prefixed. Also, it would be good if dump took a second optional argument for a description string. For example, I'd like the test expectations for this test to be something like the following:

Tests whether home moves the caret to the beginning of line inside a content-editable in an uneditable table.

Dump of markup 1:
<DIV id="test" contentEditable="true">
<#text>
</#text>
<DIV id="l1">
<#text>The caret is initially <selection-caret>here but should move to the beginning of the line.</#text>
</DIV>
<#text>
</#text>
<DIV>
<#text>dummy text</#text>
</DIV>
<#text>
</#text>
</DIV>

Dump of markup 2:
<DIV id="test" contentEditable="true">
<#text>
</#text>
<DIV id="l1">
<#text><selection-caret>The caret is initially here but should move to the beginning of the line.</#text>
</DIV>
<#text>
</#text>
<DIV>
<#text>dummy text</#text>
</DIV>
<#text>
</#text>
</DIV>

Where "Dump of markup *" is the default value for the optional description string. If Markup.dump is only called once, without a description string, then it should not print a description before dumping. Finally, if you make this change, then rename Markup._description to Markup._test_description to avoid confusion.

> +++ LayoutTests/resources/dump-as-markup.js	(working copy)

Your indentation is off in many places in this file.

> +	var markup = "";
> +
> +	if (Markup._description && !Markup._container)
> +		markup = Markup._description + '\n';

This should be += (imagine if a line got inserted above this one modifying markup). 

> +        Markup._container.style.cssText = 'width:100%';

I know you're just modifying existing code, but this should probably be "Markup._container.style.width = '100%';". No reason to use cssText if we're not actually setting multiple properties.
Comment 7 Ryosuke Niwa 2010-07-22 11:32:58 PDT
Created attachment 62320 [details]
fixed per Ojan's comment
Comment 8 Ojan Vafai 2010-07-22 13:54:43 PDT
Comment on attachment 62320 [details]
fixed per Ojan's comment

Just a couple more small things.

> +++ LayoutTests/resources/dump-as-markup.js	(working copy)
> +    if (typeof opt_node == 'string') {
> +        // Allow to specify the node by id and to pass the description in the first argument
> +        if (document.getElementById(opt_node))
> +            opt_node = document.getElementById(opt_node);
> +        else {
> +            opt_node = null;
> +            opt_description = opt_node;
> +        }

Why do we have this else clause? If opt_node is a string and the node is not in the document, we should just error.

> +    if (Markup._test_description && !Markup._container)
> +        markup += Markup._test_description + '\n';
> +
> +    Markup._dumpCalls++;
> +
> +    // If dump is called not by notifyDone or dump has already been called then
> +    // print out optional description because this is a test with multiple calls of dump.
> +    if (!Markup._done || opt_description) {
> +        if (!opt_description)
> +            opt_description = "Dump of markup " + Markup._dumpCalls
> +        if (Markup._container)

Checking Markup._container here and above is a bit confusing to me. How about checking that Markup._dumpCalls > 1? That should have the same effect, right?

> +            markup += '\n';
> +        markup += '\n' + opt_description;

Nit: Can you add add a colon after the description? I think it makes the output a bit more readable.

> -    Markup.dump();
> +    Markup._done = true;
> +
> +    // If dump has already been called, don't bother to dump again
I like this!

> +    if (!Markup._dumpCalls)
> +        Markup.dump();
Comment 9 Ryosuke Niwa 2010-07-22 14:16:34 PDT
(In reply to comment #8)
> (From update of attachment 62320 [details])
> Just a couple more small things.
> 
> > +++ LayoutTests/resources/dump-as-markup.js	(working copy)
> > +    if (typeof opt_node == 'string') {
> > +        // Allow to specify the node by id and to pass the description in the first argument
> > +        if (document.getElementById(opt_node))
> > +            opt_node = document.getElementById(opt_node);
> > +        else {
> > +            opt_node = null;
> > +            opt_description = opt_node;
> > +        }
> 
> Why do we have this else clause? If opt_node is a string and the node is not in the document, we should just error.

What if I've already called setNodeToDump and just want to pass-in the description?  Then I have to specify the node again in order to dump a subtree.  So I added that else clause to allow the description being passed through opt_node.

> > +    if (Markup._test_description && !Markup._container)
> > +        markup += Markup._test_description + '\n';
> > +
> > +    Markup._dumpCalls++;
> > +
> > +    // If dump is called not by notifyDone or dump has already been called then
> > +    // print out optional description because this is a test with multiple calls of dump.
> > +    if (!Markup._done || opt_description) {
> > +        if (!opt_description)
> > +            opt_description = "Dump of markup " + Markup._dumpCalls
> > +        if (Markup._container)
> 
> Checking Markup._container here and above is a bit confusing to me. How about checking that Markup._dumpCalls > 1? That should have the same effect, right?

Will do.

> > +            markup += '\n';
> > +        markup += '\n' + opt_description;
> 
> Nit: Can you add add a colon after the description? I think it makes the output a bit more readable.

Will do.
Comment 10 Ryosuke Niwa 2010-07-22 14:26:10 PDT
Created attachment 62339 [details]
fixed per ojan's comments
Comment 11 Ojan Vafai 2010-07-22 14:43:44 PDT
> What if I've already called setNodeToDump and just want to pass-in the description?  Then I have to specify the node again in order to dump a subtree.  So I added that else clause to allow the description being passed through opt_node.

Yikes. Having the arguments swap places like that is too confusing to be worth it. You can just do Markup.dump(null, "foo").
Comment 12 Ryosuke Niwa 2010-07-22 14:56:33 PDT
Created attachment 62345 [details]
fixed per ojan's comments

(In reply to comment #11)
> > What if I've already called setNodeToDump and just want to pass-in the description?  Then I have to specify the node again in order to dump a subtree.  So I added that else clause to allow the description being passed through opt_node.
> 
> Yikes. Having the arguments swap places like that is too confusing to be worth it. You can just do Markup.dump(null, "foo").

Ok, removed.
Comment 13 Ojan Vafai 2010-07-22 15:03:42 PDT
Comment on attachment 62345 [details]
fixed per ojan's comments

You lost your ChangeLog modifications. Please put those back in and feel free to commit.
Comment 14 Ryosuke Niwa 2010-07-22 16:21:15 PDT
Landed as http://trac.webkit.org/changeset/63918.