Bug 19795

Summary: execCommand FormatBlock creates lots of blockquotes
Product: WebKit Reporter: Nick Santos <nicksantos>
Component: HTML EditingAssignee: Ryosuke Niwa <rniwa>
Status: RESOLVED FIXED    
Severity: Normal CC: darin, enrica, jparent, justin.garcia, rniwa, tony
Priority: P2 Keywords: GoogleBug, InRadar
Version: 525.x (Safari 3.1)   
Hardware: PC   
OS: OS X 10.5   
URL: http://nick-santos.com/tests/formatblock.html
Bug Depends on: 46504, 46840, 47098, 47233, 47575    
Bug Blocks: 44918, 47712    
Attachments:
Description Flags
work in progress
none
fixes the bug
none
added minor fix to ApplyBlockElementCommand
none
fixed moveParagraphWithClones for fast/html/nav-element.html darin: review+

Description Nick Santos 2008-06-27 14:22:25 PDT
Repro steps:
1) Select a few lines of text in a contentEditable block
2) run document.execCommand('FormatBlock', false, 'blockquote')

Expected:
1 blockquote

Actual:
lots o' blockquotes
Comment 1 Mark Rowe (bdash) 2008-06-27 21:38:44 PDT
<rdar://problem/6041710>
Comment 2 Ryosuke Niwa 2010-09-25 21:41:33 PDT
Created attachment 68840 [details]
work in progress

In this patch, I tried to match the code of FormatBlock as closely as possible to that of IndentOutdentCommand.  As a result much of FormatBlockCommand is rewritten but I think this is a good direction because we can probably share a lot of code between FormatBlockCommand and IndentOutdentCommand.  Now the question is how we share the code.  One option is to extract the shared code and move it to CompositeEditCommand.  Another option is to add a base class for IndentOutdentCommand, FormatBlockCommand, & InsertListCommand that inherits from CompositeEditCommand.  Since these three commands do very similar things, we might find this approach valuable if we wanted to avoid adding specialized functions to CompositeEditCommand.

Darin, Enrica, Ojan, & Tony, any thoughts on this?
Comment 3 Darin Adler 2010-09-27 11:53:05 PDT
(In reply to comment #2)
>  Another option is to add a base class for IndentOutdentCommand, FormatBlockCommand, & InsertListCommand that inherits from CompositeEditCommand. Since these three commands do very similar things, we might find this approach valuable if we wanted to avoid adding specialized functions to CompositeEditCommand.

Either approach sounds fine to me.

If there is some data involved then we definitely would want a new class. If it’s just functions, then I think putting it in CompositeEditCommand is OK for now.
Comment 4 Ryosuke Niwa 2010-09-29 13:19:29 PDT
(In reply to comment #3)
> Either approach sounds fine to me.
> 
> If there is some data involved then we definitely would want a new class. If it’s just functions, then I think putting it in CompositeEditCommand is OK for now.

I don't think there's any data involved (except possibly tagName for FormatBlock) but I'd really like to use virtual functions.  For example, indentIntoBlockquote and indentIntoBlockNode share very similar overall structure but it needs to do slightly different things at two places.  It'll really help to isolate those two logics into helper functions and override them in IndentOutdentCommand and FormatBlockCommand.
Comment 5 Ryosuke Niwa 2010-10-07 19:49:14 PDT
Created attachment 70194 [details]
fixes the bug
Comment 6 Ryosuke Niwa 2010-10-08 01:24:30 PDT
Created attachment 70208 [details]
added minor fix to ApplyBlockElementCommand
Comment 7 Tony Chang 2010-10-12 12:32:51 PDT
Comment on attachment 70208 [details]
added minor fix to ApplyBlockElementCommand

View in context: https://bugs.webkit.org/attachment.cgi?id=70208&action=review

Some questions to help my understanding of this code.

> WebCore/editing/FormatBlockCommand.h:41
> +    static bool isElementToApplyInFormatBlockCommand(const QualifiedName& tagName);

Is this part of FormatBlockCommand because you want to merge it with the code in Editor.cpp?  Seems like it could just be in the cpp file.

> WebCore/editing/FormatBlockCommand.h:47
> +    Node* enclosingBlockToSplitTreeTo(Node* startNode) const;

This method also seems like it could be static and fully in the cpp file.

> LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:64
> +| <div>
> +|   <p>
> +|     "<#selection-anchor>hello"
> +|     <br>
> +|     "world<#selection-focus>"

Why doesn't <p> replace the <div> in this case?

> LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:180
> +|   <br>
> +|   "worl<#selection-focus>d"

This looks like it's off by a letter.  Maybe just file a bug for this case since it's <pre>.

> LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:202
> +| <div>
> +|   contenteditable=""
> +|   id="test9"
> +|   "

Did you mean to include all this in this test?

> LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:339
> +|   <ul>
> +|     <li>
> +|       "<#selection-anchor>hello"
> +|   <ul>

The merging of these ul seems like something we can try to fix in a follow up change.
Comment 8 Ryosuke Niwa 2010-10-12 12:44:44 PDT
Thanks for the review!

(In reply to comment #7)
> > WebCore/editing/FormatBlockCommand.h:41
> > +    static bool isElementToApplyInFormatBlockCommand(const QualifiedName& tagName);
> 
> Is this part of FormatBlockCommand because you want to merge it with the code in Editor.cpp?  Seems like it could just be in the cpp file.

No, this is called in WebCore/editing/EditorCommand.cpp where we invoke FormatBlockCommand.

> > WebCore/editing/FormatBlockCommand.h:47
> > +    Node* enclosingBlockToSplitTreeTo(Node* startNode) const;
> 
> This method also seems like it could be static and fully in the cpp file.

Sure.  I could do that.

> > LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:64
> > +| <div>
> > +|   <p>
> > +|     "<#selection-anchor>hello"
> > +|     <br>
> > +|     "world<#selection-focus>"
> 
> Why doesn't <p> replace the <div> in this case?

We should.  But we can't do that right now because we can't figure out whether or not div is fully selected.

> > LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:180
> > +|   <br>
> > +|   "worl<#selection-focus>d"
> 
> This looks like it's off by a letter.  Maybe just file a bug for this case since it's <pre>.

I suspect that it's a bug in text iterator.

> > LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:202
> > +| <div>
> > +|   contenteditable=""
> > +|   id="test9"
> > +|   "
> 
> Did you mean to include all this in this test?

No. Will fix.

> > LayoutTests/editing/execCommand/format-block-multiple-paragraphs-expected.txt:339
> > +|   <ul>
> > +|     <li>
> > +|       "<#selection-anchor>hello"
> > +|   <ul>
> 
> The merging of these ul seems like something we can try to fix in a follow up change.

Yes, I'm intending to fix a bunch of bugs like that in the bug 44918.  In this patch, I wanted to preserve the existing behavior as much as possible since I'm basically rewriting the entire function.
Comment 9 Ryosuke Niwa 2010-10-12 23:01:37 PDT
Committed r69639: <http://trac.webkit.org/changeset/69639>
Comment 10 Ryosuke Niwa 2010-10-13 01:10:26 PDT
Created attachment 70586 [details]
fixed moveParagraphWithClones for fast/html/nav-element.html
Comment 11 Ryosuke Niwa 2010-10-13 01:11:33 PDT
(In reply to comment #9)
> Committed r69639: <http://trac.webkit.org/changeset/69639>

It turned out that this broke fast/html/nav-element.html because moveParagraphWithClones adds extra br.  My new patch fixes this problem as well.
Comment 12 Ryosuke Niwa 2010-10-13 01:12:25 PDT
Reopening the bug since the first commit has been rolled out.
Comment 13 Darin Adler 2010-10-13 09:55:17 PDT
Comment on attachment 70586 [details]
fixed moveParagraphWithClones for fast/html/nav-element.html

View in context: https://bugs.webkit.org/attachment.cgi?id=70586&action=review

> WebCore/editing/EditorCommand.cpp:445
> +    if (!FormatBlockCommand::isElementToApplyInFormatBlockCommand(qualifiedTagName))
> +        return false;
> +
>      applyCommand(FormatBlockCommand::create(frame->document(), qualifiedTagName));

If I was doing this, I would have put this logic into FormatBlockCommand::create and had it return 0 if it’s an element it should not apply to. That seems easier to use than a separate function you have to know to call.

> WebCore/editing/FormatBlockCommand.cpp:78
> +// FIXME: We should consider mering this function with isElementForFormatBlockCommand in Editor.cpp

Typo: "mering".

> WebCore/editing/FormatBlockCommand.h:41
> +    static bool isElementToApplyInFormatBlockCommand(const QualifiedName& tagName);

I don’t understand the phrase “element to apply in command”. What does that mean?
Comment 14 Ryosuke Niwa 2010-10-13 11:27:24 PDT
(In reply to comment #13)
> > WebCore/editing/EditorCommand.cpp:445
> > +    if (!FormatBlockCommand::isElementToApplyInFormatBlockCommand(qualifiedTagName))
> > +        return false;
> > +
> >      applyCommand(FormatBlockCommand::create(frame->document(), qualifiedTagName));
> 
> If I was doing this, I would have put this logic into FormatBlockCommand::create and had it return 0 if it’s an element it should not apply to. That seems easier to use than a separate function you have to know to call.

I like that design better too.  I was considering to do it in this patch as well but then this patch was already pretty big so I avoided it.  I'll probably do that when I merge isElementToApplyInFormatBlockCommand and isElementForFormatBlockCommand.

> > WebCore/editing/FormatBlockCommand.cpp:78
> > +// FIXME: We should consider mering this function with isElementForFormatBlockCommand in Editor.cpp
> 
> Typo: "mering".

Thanks.  Will fix.

> > WebCore/editing/FormatBlockCommand.h:41
> > +    static bool isElementToApplyInFormatBlockCommand(const QualifiedName& tagName);
> 
> I don’t understand the phrase “element to apply in command”. What does that mean?

We're checking whether or not an element can be applied using format block command or whether or not the element should be replaced by new element.  I think isElementForFormatBlockCommand is a better name but I didn't want to use the same name.  I'll merge this function with isElementForFormatBlockCommand so this function should be gone in no time.
Comment 15 Ryosuke Niwa 2010-10-14 21:10:04 PDT
Committed r69836: <http://trac.webkit.org/changeset/69836>