Reviewed by Adele and Justin.
- test for http://bugzilla.opendarwin.org/show_bug.cgi?id=9630
REGRESSION: some spaces typed in <textarea> are posted as non-breaking spaces
* fast/forms/textarea-type-spaces-expected.txt: Added.
* fast/forms/textarea-type-spaces.html: Added.
- test for http://bugzilla.opendarwin.org/show_bug.cgi?id=9939
REGRESSION: Pasting text into native text area with newline at end does not preserve newline
* fast/forms/textarea-paste-newline-expected.txt: Added.
* fast/forms/textarea-paste-newline.html: Added.
- updated tests affected by above changes, results are equivalent or better
* editing/pasteboard/
4641033-expected.checksum:
* editing/pasteboard/
4641033-expected.png:
* editing/pasteboard/
4641033-expected.txt:
* editing/pasteboard/paste-table-003-expected.checksum:
* editing/pasteboard/paste-table-003-expected.png:
* editing/pasteboard/paste-table-003-expected.txt:
* editing/pasteboard/paste-text-016-expected.txt:
* editing/pasteboard/quirks-mode-br-1-expected.txt:
* editing/pasteboard/quirks-mode-br-2-expected.txt:
* fast/forms/textarea-scrolled-type-expected.checksum:
* fast/forms/textarea-scrolled-type-expected.png:
* fast/forms/textarea-scrolled-type-expected.txt:
- changed a test to be a "dump as text" test
* fast/forms/paste-into-textarea-expected.txt:
* fast/forms/paste-into-textarea.html:
* fast/forms/paste-into-textarea-expected.checksum: Removed.
* fast/forms/paste-into-textarea-expected.png: Removed.
- changed a test to be a "dump as text" test, improved test a bit and also checked in new results
* fast/forms/textarea-scrolled-endline-caret-expected.txt:
* fast/forms/textarea-scrolled-endline-caret.html:
- fixed a test that was raising an exception every time it ran
* fast/forms/attributed-strings-expected.txt:
* fast/forms/attributed-strings.html:
- corrected checksums on a bunch of tests (pngs were right, but checksums wrong, apparently)
* editing/deleting/delete-to-select-table-expected.checksum:
* editing/selection/
3690719-expected.checksum:
* editing/selection/clear-selection-expected.checksum:
* editing/undo/undo-misspellings-expected.checksum:
- added some missing pixel test results
* editing/pasteboard/nested-blocks-with-text-area-expected.checksum: Added.
* editing/pasteboard/nested-blocks-with-text-area-expected.png: Added.
* editing/pasteboard/nested-blocks-with-text-field-expected.checksum: Added.
* editing/pasteboard/nested-blocks-with-text-field-expected.png: Added.
WebCore:
Reviewed by Adele and Justin.
- fix <rdar://problem/
4613616> REGRESSION: some spaces typed in <textarea> are posted as non-breaking spaces (9630)
http://bugzilla.opendarwin.org/show_bug.cgi?id=9630
- also fixes http://bugzilla.opendarwin.org/show_bug.cgi?id=9939
REGRESSION: Pasting text into native text area with newline at end does not preserve newline
- removed some uses of DeprecatedPtrList in the markup code
Test: fast/forms/textarea-type-spaces.html
Test: fast/forms/textarea-paste-newline.html
* bindings/objc/DOMHTML.mm: (-[DOMHTMLDocument createDocumentFragmentWithText:]):
Updated call to pass a range -- in this case it is the range of the entire document,
so this will not handle the whitespace properly.
* bridge/mac/WebCoreFrameBridge.h: Added range context parameter to the
documentFragmentWithText: method, so we can handle whitespace properly.
* bridge/mac/WebCoreFrameBridge.mm:
(-[WebCoreFrameBridge nodesFromList:]): Changed from DeprecatedPtrList to Vector.
(-[WebCoreFrameBridge markupStringFromNode:nodes:]): Ditto.
(-[WebCoreFrameBridge markupStringFromRange:nodes:]): Ditto.
(-[WebCoreFrameBridge documentFragmentWithText:inContext:]): Added range context
parameter -- pass it on to createFragmentFromText.
(-[WebCoreFrameBridge documentFragmentWithNodesAsParagraphs:]): Changed from
DeprecatedPtrList to Vector.
(-[WebCoreFrameBridge replaceSelectionWithText:selectReplacement:smartReplace:]):
Pass the range of the current selection as context when creating the fragment.
* dom/Position.cpp: (WebCore::Position::inRenderedText): Replace range check with
a call to the new containsCaretOffset function -- helps make the caret work right when
it is past the end of the last line in a textarea.
* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::rebalanceWhitespaceAt): Don't do anything if the
style does not call for collapsing whitespace.
(WebCore::CompositeEditCommand::rebalanceWhitespace): Call replaceWhitespaceAt
to share code, including the new logic mentioned above.
* editing/InsertLineBreakCommand.cpp: (WebCore::InsertLineBreakCommand::doApply):
Use a text node instead of a break element when inserting and the style is preserveNewline.
* editing/JSEditor.cpp: (WebCore::execRemoveFormat): Pass the selection range
to createFragmentFromText.
* editing/RebalanceWhitespaceCommand.cpp: (WebCore::RebalanceWhitespaceCommand::doApply):
Assert that we're in a style that collapses whitespace. It's the caller's responsibility
not to call otherwise.
* editing/ReplaceSelectionCommand.h: Removed unused destructor, type, isSingleTextNode,
isTreeFragment, m_type, and added a context parameter to inertFragmentForTestRendering.
Also changed the constructor to take a selection rather than a pointer to the root
editable element, replaced removeEndBRIfNeeded with shouldRemoveEndBR and removed an
unused parameter from shouldMergeEnd.
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplacementFragment::ReplacementFragment): Removed code to set up m_type.
Compute root editable element from passed-in selection. Used the start of the selection
as a base node for style purposes for the test rendering. Removed the special case
"single text node" alternative to createFragmentFromText in the plain text case, since
createFragmentFromText now handles that correctly.
(WebCore::ReplacementFragment::insertFragmentForTestRendering): Copy the whitespace
property from the source location when creating a temporary element for test rendering.
(WebCore::ReplacementFragment::shouldMergeEnd): Removed unneeded boolean
parameter fragmentHadInterchangeNewlineAtEnd, which is always false.
(WebCore::ReplaceSelectionCommand::doApply): Update for ReplacementFragment changes,
change code to not remove end BR when it can be re-used instead, don't call the
paragraph separator insertion when the position is at the start of a paragraph already,
removed redundant computation of identical "next" value, removed unneeded boolean
parameter to shouldMergeEnd, add case for merging when all we need to do is to delete
a newline character, removed unneeded code to set insertionPos after all code that uses
it, and use spaces instead of non-breaking spaces when doing smart paste if the
context is one where we do not collapse white space.
(WebCore::ReplaceSelectionCommand::shouldRemoveEndBR): Renamed and changed to return
a boolean instead of doing the removal.
* editing/markup.h: Use Vector instead of DeprecatedPtrList. Change the
createFragmentFromText function to take a range for context instead of a document.
* editing/markup.cpp:
(WebCore::markup): Use Vector instead of DeprecatedPtrList.
(WebCore::createMarkup): Ditto.
(WebCore::createParagraphContentsFromString): Remove unneeded document parameter
and changed a couple places to use isEmpty instead of comparing with "".
(WebCore::createFragmentFromText): Given the new context parameter, if the context
is one that preserves newlines, then use "\n" instead of <br> elements.
(WebCore::createFragmentFromNodes): Use Vector instead of DeprecatedPtrList.
* html/HTMLElement.cpp: (WebCore::HTMLElement::setInnerText): Do not use <br>
elements if the context of this node is one where we preserve newlines.
* rendering/InlineTextBox.h:
* rendering/InlineTextBox.cpp: (WebCore::InlineTextBox::containsCaretOffset):
Added. Implements the appropriate rule for determining if a caret position is
in this line or not. Returns true for both one line and the next in cases where
affinity must be considered to determine which line the caret is on.
* rendering/RenderText.h: Make atLineWrap no longer be a member function.
* rendering/RenderText.cpp:
(WebCore::atLineWrap): Remove special rule about preserveNewline and isLineBreak,
which will no longer apply due to the new containsCaretOffset function logic.
(WebCore::RenderText::caretRect): Use containsCaretOffset.
(WebCore::RenderText::inlineBox): Ditto.
* rendering/RenderTextControl.cpp:
(WebCore::RenderTextControl::updateFromElement): Make a placeholder <br> element
after calling setInnerText so that the last newline in the string has the effect
we expect outside the HTML world (an additional line).
(WebCore::RenderTextControl::setSelectionRange): Set granularity of the selection
too. The layout tests caught this problem, which needs a better solution long term.
(WebCore::RenderTextControl::text): Call textContent with the parameter false
so it will not include newlines for <br> elements. Now the only <br> element
that will ever be in the shadow DOM tree is the one to prevent collapsing, and
that one should not show up in the text value.
* rendering/bidi.cpp: (WebCore::RenderBlock::findNextLineBreak): Took a rule
that specifically called out the pre whitespace mode and made it work for all
the modes that preserve newlines. This makes sure we get a last line box for
text after the last "\n" even in cases where there is no <br> afterward.
* editing/DeleteSelectionCommand.cpp:
(WebCore::DeleteSelectionCommand::fixupWhitespace):
* editing/InsertParagraphSeparatorCommand.cpp:
(WebCore::InsertParagraphSeparatorCommand::doApply):
Added assertions to make sure we don't do anything when we're not collapsing
whitespace.
* html/HTMLTextAreaElement.cpp: (WebCore::HTMLTextAreaElement::setDefaultValue):
Changed to use Vector instead of DeprecatedPtrList.
* editing/HTMLInterchange.cpp: Removed obsolete comment.
* loader/Cache.h: Removed a stray include.
WebKit:
Reviewed by Adele and Justin.
- update for change to require context when creating fragments from text
(needed to handle whitespace properly)
* WebView/WebHTMLView.m:
(-[WebHTMLView _documentFragmentFromPasteboard:inContext:allowPlainText:chosePlainText:]):
Added context parameter, pass through to bridge.
(-[WebHTMLView _pasteWithPasteboard:allowPlainText:]): Pass selection range as context
when calling above method.
(-[WebHTMLView concludeDragForDraggingInfo:actionMask:]): Pass drag caret as context when
calling above method.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@15617
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2006-07-24 Darin Adler <darin@apple.com>
+
+ Reviewed by Adele and Justin.
+
+ - test for http://bugzilla.opendarwin.org/show_bug.cgi?id=9630
+ REGRESSION: some spaces typed in <textarea> are posted as non-breaking spaces
+
+ * fast/forms/textarea-type-spaces-expected.txt: Added.
+ * fast/forms/textarea-type-spaces.html: Added.
+
+ - test for http://bugzilla.opendarwin.org/show_bug.cgi?id=9939
+ REGRESSION: Pasting text into native text area with newline at end does not preserve newline
+
+ * fast/forms/textarea-paste-newline-expected.txt: Added.
+ * fast/forms/textarea-paste-newline.html: Added.
+
+ - updated tests affected by above changes, results are equivalent or better
+
+ * editing/pasteboard/4641033-expected.checksum:
+ * editing/pasteboard/4641033-expected.png:
+ * editing/pasteboard/4641033-expected.txt:
+ * editing/pasteboard/paste-table-003-expected.checksum:
+ * editing/pasteboard/paste-table-003-expected.png:
+ * editing/pasteboard/paste-table-003-expected.txt:
+ * editing/pasteboard/paste-text-016-expected.txt:
+ * editing/pasteboard/quirks-mode-br-1-expected.txt:
+ * editing/pasteboard/quirks-mode-br-2-expected.txt:
+ * fast/forms/textarea-scrolled-type-expected.checksum:
+ * fast/forms/textarea-scrolled-type-expected.png:
+ * fast/forms/textarea-scrolled-type-expected.txt:
+
+ - changed a test to be a "dump as text" test
+
+ * fast/forms/paste-into-textarea-expected.txt:
+ * fast/forms/paste-into-textarea.html:
+ * fast/forms/paste-into-textarea-expected.checksum: Removed.
+ * fast/forms/paste-into-textarea-expected.png: Removed.
+
+ - changed a test to be a "dump as text" test, improved test a bit and also checked in new results
+
+ * fast/forms/textarea-scrolled-endline-caret-expected.txt:
+ * fast/forms/textarea-scrolled-endline-caret.html:
+
+ - fixed a test that was raising an exception every time it ran
+
+ * fast/forms/attributed-strings-expected.txt:
+ * fast/forms/attributed-strings.html:
+
+ - corrected checksums on a bunch of tests (pngs were right, but checksums wrong, apparently)
+
+ * editing/deleting/delete-to-select-table-expected.checksum:
+ * editing/selection/3690719-expected.checksum:
+ * editing/selection/clear-selection-expected.checksum:
+ * editing/undo/undo-misspellings-expected.checksum:
+
+ - added some missing pixel test results
+
+ * editing/pasteboard/nested-blocks-with-text-area-expected.checksum: Added.
+ * editing/pasteboard/nested-blocks-with-text-area-expected.png: Added.
+ * editing/pasteboard/nested-blocks-with-text-field-expected.checksum: Added.
+ * editing/pasteboard/nested-blocks-with-text-field-expected.png: Added.
+
2006-07-24 Adele Peterson <adele@apple.com>
Reviewed by Justin.
-4f3c4bb088358dbb7dd79f971a4a41b8
\ No newline at end of file
+6ac121385e85689201493f064d0014f2
\ No newline at end of file
-827eed3249a8431338e7c6ea96dc90d8
\ No newline at end of file
+9a95881ba32b5b25fa40bd8cf30fa1d4
\ No newline at end of file
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 0 of DIV > BODY > HTML > #document to 0 of DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > BODY > HTML > #document to 0 of DIV > BODY > HTML > #document toDOMRange:range from 0 of DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > BODY > HTML > #document to 0 of DIV > BODY > HTML > #document toDOMRange:range from 3 of DIV > BODY > HTML > #document to 3 of DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
text run at (251,0) width 384: " Copy/paste of a select element fails to include the options"
RenderText {#text} at (635,0) size 4x18
text run at (635,0) width 4: "."
- RenderBlock {DIV} at (0,86) size 784x128
- RenderBlock (anonymous) at (0,0) size 784x110
- RenderImage {IMG} at (0,0) size 76x103
- RenderText {#text} at (76,89) size 4x18
- text run at (76,89) width 4: " "
- RenderMenuList {SELECT} at (82,90) size 36x18
- RenderBlock {DIV} at (0,110) size 784x18
- RenderBR {BR} at (0,0) size 0x18
- RenderBlock (anonymous) at (0,214) size 784x110
+ RenderBlock {DIV} at (0,86) size 784x110
+ RenderImage {IMG} at (0,0) size 76x103
+ RenderText {#text} at (76,89) size 4x18
+ text run at (76,89) width 4: " "
+ RenderMenuList {SELECT} at (82,90) size 36x18
+ RenderBlock (anonymous) at (0,196) size 784x110
RenderImage {IMG} at (0,0) size 76x103
RenderText {#text} at (76,89) size 4x18
text run at (76,89) width 4: " "
text run at (0,0) width 19: " 1"
RenderText {#text} at (0,0) size 0x0
RenderText {#text} at (0,0) size 0x0
-caret: position 0 of child 0 {BR} of child 3 {DIV} of child 4 {DIV} of child 0 {BODY} of child 0 {HTML} of document
+caret: position 1 of child 2 {SELECT} of child 4 {DIV} of child 0 {BODY} of child 0 {HTML} of document
--- /dev/null
+fd30caa739fed6b0a841c8fc21115434
\ No newline at end of file
--- /dev/null
+81191b6afbb0d57245c89090b4228287
\ No newline at end of file
-00eb248dca688216f1341399284e4d66
\ No newline at end of file
+9697e5aa6aff2be0a6d9c1e2098d3c89
\ No newline at end of file
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 0 of #text > DIV > BODY > HTML > #document to 9 of #text > DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 2 of TD > TR > TBODY > TABLE > DIV > BODY > HTML > #document to 2 of TD > TR > TBODY > TABLE > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of DIV > BODY > HTML > #document to 0 of DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
RenderTableCell {TD} at (29,2) size 26x20 [r=0 c=1 rs=1 cs=1]
RenderText {#text} at (1,1) size 24x18
text run at (1,1) width 24: "two"
- RenderBlock {DIV} at (0,24) size 784x60
- RenderTable {TABLE} at (0,0) size 57x42
- RenderTableSection {TBODY} at (0,0) size 57x42
- RenderTableRow {TR} at (0,2) size 57x38
- RenderTableCell {TD} at (2,11) size 25x20 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (0,24) size 784x42
+ RenderTable {TABLE} at (0,0) size 57x24
+ RenderTableSection {TBODY} at (0,0) size 57x24
+ RenderTableRow {TR} at (0,2) size 57x20
+ RenderTableCell {TD} at (2,2) size 25x20 [r=0 c=0 rs=1 cs=1]
RenderText {#text} at (1,1) size 23x18
text run at (1,1) width 23: "one"
- RenderTableCell {TD} at (29,2) size 26x38 [r=0 c=1 rs=1 cs=1]
+ RenderTableCell {TD} at (29,2) size 26x20 [r=0 c=1 rs=1 cs=1]
RenderText {#text} at (1,1) size 24x18
text run at (1,1) width 24: "two"
- RenderBR {BR} at (25,15) size 0x0
- RenderBR {BR} at (1,19) size 0x18
- RenderBlock (anonymous) at (0,42) size 784x18
+ RenderBlock (anonymous) at (0,24) size 784x18
RenderBR {BR} at (0,0) size 0x18
-caret: position 0 of child 2 {BR} of child 1 {TD} of child 0 {TR} of child 0 {TBODY} of child 0 {TABLE} of child 2 {DIV} of child 1 {BODY} of child 0 {HTML} of document
+ RenderBlock {DIV} at (0,66) size 784x18
+ RenderBR {BR} at (0,0) size 0x18
+caret: position 0 of child 0 {BR} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 2 of P > DIV > DIV > BODY > HTML > #document to 2 of P > DIV > DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of P > P > DIV > DIV > BODY > HTML > #document to 0 of P > P > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of P > DIV > DIV > BODY > HTML > #document to 3 of P > DIV > DIV > BODY > HTML > #document toDOMRange:range from 3 of P > DIV > DIV > BODY > HTML > #document to 3 of P > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
RenderBlock {P} at (0,28) size 756x28
RenderText {#text} at (0,0) size 130x28
text run at (0,0) width 130: "***TEST***"
- RenderBlock {P} at (0,56) size 756x28
+ RenderBlock (anonymous) at (0,56) size 756x56
RenderBR {BR} at (0,0) size 0x28
- RenderBlock (anonymous) at (0,84) size 756x28
- RenderText {#text} at (0,0) size 128x28
- text run at (0,0) width 128: "Another line."
+ RenderText {#text} at (0,28) size 128x28
+ text run at (0,28) width 128: "Another line."
RenderBlock {P} at (14,126) size 756x0
RenderBlock (anonymous) at (14,126) size 756x28
RenderText {#text} at (0,0) size 6x28
text run at (0,0) width 6: " "
RenderBlock {P} at (14,182) size 756x28
RenderBR {BR} at (0,0) size 0x28
-caret: position 0 of child 0 {BR} of child 3 {P} of child 0 {P} of child 1 {DIV} of child 7 {DIV} of child 1 {BODY} of child 0 {HTML} of document
+caret: position 0 of child 3 {BR} of child 0 {P} of child 1 {DIV} of child 7 {DIV} of child 1 {BODY} of child 0 {HTML} of document
RenderText {#text} at (0,0) size 370x18
text run at (0,0) width 370: "The test should add a single blank line after this paragraph."
RenderBR {BR} at (370,14) size 0x0
- RenderBR {BR} at (0,18) size 0x18
RenderInline {SPAN} at (0,0) size 0x0
-caret: position 0 of child 2 {BR} of child 0 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
+ RenderBR {BR} at (0,18) size 0x18
+caret: position 0 of child 3 {BR} of child 0 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 1 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document toDOMRange:range from 1 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
-2762a21712500c513288a4796e7c6f0d
\ No newline at end of file
+e93d8592f6229d3c0681c600617c9f4d
\ No newline at end of file
-675958ca1760e67d565768737724a7ed
\ No newline at end of file
+b6430b633335a2e5a40c5bae7d791546
\ No newline at end of file
-2a251ca75a4ee28206fbc9f203a921e3
\ No newline at end of file
+8a9b3e46242b36500276135ba1c29d62
\ No newline at end of file
NSToolTip,NSForegroundColor,NSFont
0.5
undefined
-undefined
NSDeviceRGBColorSpace 1 0.5 1 0.5
log(attrString.getAttributeValueAtIndex("NSObliqueness", 0));
log(attrString.getAttributeValueAtIndex("NSObliqueness", 3));
- log(attrString.getAttributeValueAtIndex("NSObliqueness", 10));
log(attrString.getAttributeValueAtIndex("NSForegroundColor", 1));
+++ /dev/null
-2dd265383d1c568785c1c7ae84dfdaf6
\ No newline at end of file
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 57 of #text > DIV to 57 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV to 1 of #text > DIV toDOMRange:range from 57 of #text > DIV to 57 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
- RenderView at (0,0) size 800x600
-layer at (0,0) size 800x600
- RenderBlock {HTML} at (0,0) size 800x600
- RenderBody {BODY} at (8,8) size 784x584
- RenderBlock {P} at (0,0) size 784x18
- RenderText {#text} at (0,0) size 757x18
- text run at (0,0) width 757: "This tests for a bug where text pasted into a textarea would appear one character before the position where it was pasted."
- RenderBlock (anonymous) at (0,34) size 784x71
- RenderTextField {TEXTAREA} at (2,2) size 163x67 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
- RenderText {#text} at (0,0) size 0x0
-layer at (11,45) size 161x65
- RenderBlock {DIV} at (1,1) size 161x65
- RenderText {#text} at (3,0) size 7x13
- text run at (3,0) width 7: "x"
- RenderText {#text} at (10,0) size 140x39
- text run at (10,0) width 133: "(There should be one 'x' "
- text run at (3,13) width 112: "before and after this "
- text run at (3,26) width 55: "sentence.)"
- RenderText {#text} at (58,26) size 7x13
- text run at (58,26) width 7: "x"
-caret: position 57 of child 1 {#text} of child 0 {DIV} of child 2 {TEXTAREA} of child 0 {BODY} of child 0 {HTML} of document
+This tests for a bug where text pasted into a textarea would appear one character before the position where it was pasted.
+
+Hooray, test succeeded.
+
+
<p>This tests for a bug where text pasted into a textarea would appear one character before the position where it was pasted.</p>
<textarea rows=5 id="test">xx</textarea>
-
<script>
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
var e = document.getElementById("test");
e.setSelectionRange(1, 1);
-
document.execCommand("InsertHTML", false, "(There should be one 'x' before and after this sentence.)");
-</script>
\ No newline at end of file
+if (e.value == "x(There should be one 'x' before and after this sentence.)x")
+ document.write("<p>Hooray, test succeeded.</p>");
+else
+ document.write("<p>Test failed, value is '" + e.value + "'.</p>");
+</script>
--- /dev/null
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldDeleteDOMRange:range from 0 of #text > DIV to 4 of #text > DIV
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of DIV to 0 of DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: shouldInsertText:abc
+ replacingDOMRange:range from 0 of DIV to 0 of DIV givenAction:WebViewInsertActionPasted
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 4 of #text > DIV to 4 of #text > DIV toDOMRange:range from 4 of #text > DIV to 4 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldInsertText:abc
+ replacingDOMRange:range from 0 of #text > DIV to 0 of #text > DIV givenAction:WebViewInsertActionPasted
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 4 of #text > DIV to 4 of #text > DIV toDOMRange:range from 4 of #text > DIV to 4 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+Hooray, the test was successful!
--- /dev/null
+<script>
+function test()
+{
+ if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+ var ta = document.getElementById("ta");
+ ta.value = "abc\n";
+ ta.focus();
+ ta.setSelectionRange(0, 4);
+ document.execCommand("cut");
+ document.execCommand("paste");
+ var result1 = ta.value;
+ ta.setSelectionRange(0, 0);
+ document.execCommand("paste");
+ var result2 = ta.value;
+
+ if (result1 == "abc\n" && result2 == "abc\nabc\n")
+ document.write("<p>Hooray, the test was successful!</p>");
+ else if (result1 == "")
+ document.write("<p>The test failed; doesn't work in release builds of Safari because paste is not allowed.</p>");
+ else
+ document.write("<p>The test failed, result 1 was '" + result1.replace("\n", "\\n") + "' and result 2 was '" + result2.replace("\n", "\\n") + "'.</p>");
+}
+</script>
+<body onload="test()">
+<p><textarea id="ta"></textarea></p>
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 2 of #text > DIV to 2 of #text > DIV toDOMRange:range from 1 of #text > DIV to 1 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 20 of #text > DIV to 20 of #text > DIV toDOMRange:range from 17 of #text > DIV to 17 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
Test Succeeded
+
<script>
function test()
{
+ if (window.layoutTestController)
+ layoutTestController.dumpAsText();
var ta = document.getElementById('ta')
+ var res = document.getElementById('res');
ta.focus();
// click
- if (window.layoutTestController) {
- layoutTestController.dumpAsText();
+ if (window.eventSender) {
eventSender.mouseMoveTo(90, 20);
eventSender.mouseDown();
eventSender.mouseUp();
+ if (ta.selectionEnd == 17)
+ res.innerHTML = "Test Succeeded";
+ else
+ res.innerHTML = "Test Failed: caret is at " + ta.selectionEnd;
+ } else {
+ res.innerHTML = "Test can't run without event sender (part of DumpRenderTree). "
+ + "To test manually, click at the middle of the line marked 9 and check that the caret appears after the 9.";
}
- var res = document.getElementById('res');
- if (ta.selectionEnd == 17)
- res.innerHTML = "Test Succeeded";
- else
- res.innerHTML = "Test Failed: caret is at " + ta.selectionEnd;
}
</script>
</head>
7
8
9
-10
-</textarea>
-<div id="res"></div>
\ No newline at end of file
+10</textarea>
+<div id="res"></div>
+</body>
-7f38cf2e654f194ce1a95e256537fbfe
\ No newline at end of file
+bb1e86fd2232a0b2c6bda501f303b1de
\ No newline at end of file
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 2 of #text > DIV to 2 of #text > DIV toDOMRange:range from 3 of #text > DIV to 3 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 44 of #text > DIV to 44 of #text > DIV toDOMRange:range from 45 of #text > DIV to 45 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of #text > DIV to 3 of #text > DIV toDOMRange:range from 4 of #text > DIV to 4 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 45 of #text > DIV to 45 of #text > DIV toDOMRange:range from 46 of #text > DIV to 46 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 4 of #text > DIV to 4 of #text > DIV toDOMRange:range from 5 of #text > DIV to 5 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 46 of #text > DIV to 46 of #text > DIV toDOMRange:range from 47 of #text > DIV to 47 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 5 of #text > DIV to 5 of #text > DIV toDOMRange:range from 6 of #text > DIV to 6 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 47 of #text > DIV to 47 of #text > DIV toDOMRange:range from 48 of #text > DIV to 48 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 6 of #text > DIV to 6 of #text > DIV toDOMRange:range from 7 of #text > DIV to 7 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 48 of #text > DIV to 48 of #text > DIV toDOMRange:range from 49 of #text > DIV to 49 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
text run at (167,101) width 4: " "
RenderBR {BR} at (171,115) size 0x0
RenderBlock {DIV} at (0,119) size 784x0
-layer at (11,29) size 161x91 clip at (11,29) size 146x91 scrollY 169 scrollHeight 260
+layer at (11,29) size 161x91 clip at (11,29) size 146x91 scrollY 182 scrollHeight 273
RenderBlock {DIV} at (1,1) size 161x91
- RenderText {#text} at (3,0) size 7x13
+ RenderText {#text} at (3,0) size 41x260
text run at (3,0) width 7: "1"
- RenderBR {BR} at (10,11) size 0x0
- RenderText {#text} at (3,13) size 7x13
+ text run at (10,0) width 0: " "
text run at (3,13) width 7: "2"
- RenderBR {BR} at (10,24) size 0x0
- RenderText {#text} at (3,26) size 7x13
+ text run at (10,13) width 0: " "
text run at (3,26) width 7: "3"
- RenderBR {BR} at (10,37) size 0x0
- RenderText {#text} at (3,39) size 7x13
+ text run at (10,26) width 0: " "
text run at (3,39) width 7: "4"
- RenderBR {BR} at (10,50) size 0x0
- RenderText {#text} at (3,52) size 7x13
+ text run at (10,39) width 0: " "
text run at (3,52) width 7: "5"
- RenderBR {BR} at (10,63) size 0x0
- RenderText {#text} at (3,65) size 7x13
+ text run at (10,52) width 0: " "
text run at (3,65) width 7: "6"
- RenderBR {BR} at (10,76) size 0x0
- RenderText {#text} at (3,78) size 7x13
+ text run at (10,65) width 0: " "
text run at (3,78) width 7: "7"
- RenderBR {BR} at (10,89) size 0x0
- RenderText {#text} at (3,91) size 7x13
+ text run at (10,78) width 0: " "
text run at (3,91) width 7: "8"
- RenderBR {BR} at (10,102) size 0x0
- RenderText {#text} at (3,104) size 7x13
+ text run at (10,91) width 0: " "
text run at (3,104) width 7: "9"
- RenderBR {BR} at (10,115) size 0x0
- RenderText {#text} at (3,117) size 14x13
+ text run at (10,104) width 0: " "
text run at (3,117) width 14: "10"
- RenderBR {BR} at (17,128) size 0x0
- RenderText {#text} at (3,130) size 14x13
+ text run at (17,117) width 0: " "
text run at (3,130) width 14: "11"
- RenderBR {BR} at (17,141) size 0x0
- RenderText {#text} at (3,143) size 14x13
+ text run at (17,130) width 0: " "
text run at (3,143) width 14: "12"
- RenderBR {BR} at (17,154) size 0x0
- RenderText {#text} at (3,156) size 14x13
+ text run at (17,143) width 0: " "
text run at (3,156) width 14: "13"
- RenderBR {BR} at (17,167) size 0x0
- RenderText {#text} at (3,169) size 14x13
+ text run at (17,156) width 0: " "
text run at (3,169) width 14: "14"
- RenderBR {BR} at (17,180) size 0x0
- RenderText {#text} at (3,182) size 14x13
+ text run at (17,169) width 0: " "
text run at (3,182) width 14: "15"
- RenderBR {BR} at (17,193) size 0x0
- RenderText {#text} at (3,195) size 14x13
+ text run at (17,182) width 0: " "
text run at (3,195) width 14: "16"
- RenderBR {BR} at (17,206) size 0x0
- RenderText {#text} at (3,208) size 14x13
+ text run at (17,195) width 0: " "
text run at (3,208) width 14: "17"
- RenderBR {BR} at (17,219) size 0x0
- RenderText {#text} at (3,221) size 41x13
+ text run at (17,208) width 0: " "
text run at (3,221) width 41: "18 Pass"
- RenderBR {BR} at (44,232) size 0x0
- RenderText {#text} at (3,234) size 14x13
+ text run at (44,221) width 0: " "
text run at (3,234) width 14: "19"
- RenderBR {BR} at (17,245) size 0x0
- RenderText {#text} at (3,247) size 14x13
+ text run at (17,234) width 0: " "
text run at (3,247) width 14: "20"
- RenderBR {BR} at (17,258) size 0x0
-caret: position 7 of child 34 {#text} of child 0 {DIV} of child 3 {TEXTAREA} of child 1 {BODY} of child 0 {HTML} of document
+ text run at (17,247) width 0: " "
+ RenderBR {BR} at (3,260) size 0x13
+caret: position 49 of child 0 {#text} of child 0 {DIV} of child 3 {TEXTAREA} of child 1 {BODY} of child 0 {HTML} of document
--- /dev/null
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldInsertText: replacingDOMRange:range from 0 of DIV to 0 of DIV givenAction:WebViewInsertActionTyped
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV to 0 of DIV toDOMRange:range from 1 of #text > DIV to 1 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: shouldInsertText: replacingDOMRange:range from 1 of #text > DIV to 1 of #text > DIV givenAction:WebViewInsertActionTyped
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > DIV to 1 of #text > DIV toDOMRange:range from 2 of #text > DIV to 2 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: shouldInsertText: replacingDOMRange:range from 2 of #text > DIV to 2 of #text > DIV givenAction:WebViewInsertActionTyped
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 2 of #text > DIV to 2 of #text > DIV toDOMRange:range from 3 of #text > DIV to 3 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: shouldInsertText: replacingDOMRange:range from 3 of #text > DIV to 3 of #text > DIV givenAction:WebViewInsertActionTyped
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of #text > DIV to 3 of #text > DIV toDOMRange:range from 4 of #text > DIV to 4 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+Hooray, test worked!
+
+
--- /dev/null
+<p><textarea id="ta"></textarea></p>
+<script>
+if (window.layoutTestController)
+ layoutTestController.dumpAsText();
+var ta = document.getElementById("ta");
+ta.focus();
+if (window.eventSender) {
+ eventSender.keyDown(' ', new Array());
+ eventSender.keyDown(' ', new Array());
+ eventSender.keyDown(' ', new Array());
+ eventSender.keyDown(' ', new Array());
+ var result = ta.value;
+ if (result == " ")
+ document.write("<p>Hooray, test worked!</p>");
+ else
+ document.write("<pre>Test failed, result was '" + result + "'.</pre>");
+} else {
+ document.write("<p>Test can't run without event sender, part of DumpRenderTree.</p>");
+}
+</script>
+2006-07-24 Darin Adler <darin@apple.com>
+
+ Reviewed by Adele and Justin.
+
+ - fix <rdar://problem/4613616> REGRESSION: some spaces typed in <textarea> are posted as non-breaking spaces (9630)
+ http://bugzilla.opendarwin.org/show_bug.cgi?id=9630
+ - also fixes http://bugzilla.opendarwin.org/show_bug.cgi?id=9939
+ REGRESSION: Pasting text into native text area with newline at end does not preserve newline
+ - removed some uses of DeprecatedPtrList in the markup code
+
+ Test: fast/forms/textarea-type-spaces.html
+ Test: fast/forms/textarea-paste-newline.html
+
+ * bindings/objc/DOMHTML.mm: (-[DOMHTMLDocument createDocumentFragmentWithText:]):
+ Updated call to pass a range -- in this case it is the range of the entire document,
+ so this will not handle the whitespace properly.
+
+ * bridge/mac/WebCoreFrameBridge.h: Added range context parameter to the
+ documentFragmentWithText: method, so we can handle whitespace properly.
+ * bridge/mac/WebCoreFrameBridge.mm:
+ (-[WebCoreFrameBridge nodesFromList:]): Changed from DeprecatedPtrList to Vector.
+ (-[WebCoreFrameBridge markupStringFromNode:nodes:]): Ditto.
+ (-[WebCoreFrameBridge markupStringFromRange:nodes:]): Ditto.
+ (-[WebCoreFrameBridge documentFragmentWithText:inContext:]): Added range context
+ parameter -- pass it on to createFragmentFromText.
+ (-[WebCoreFrameBridge documentFragmentWithNodesAsParagraphs:]): Changed from
+ DeprecatedPtrList to Vector.
+ (-[WebCoreFrameBridge replaceSelectionWithText:selectReplacement:smartReplace:]):
+ Pass the range of the current selection as context when creating the fragment.
+
+ * dom/Position.cpp: (WebCore::Position::inRenderedText): Replace range check with
+ a call to the new containsCaretOffset function -- helps make the caret work right when
+ it is past the end of the last line in a textarea.
+
+ * editing/CompositeEditCommand.cpp:
+ (WebCore::CompositeEditCommand::rebalanceWhitespaceAt): Don't do anything if the
+ style does not call for collapsing whitespace.
+ (WebCore::CompositeEditCommand::rebalanceWhitespace): Call replaceWhitespaceAt
+ to share code, including the new logic mentioned above.
+
+ * editing/InsertLineBreakCommand.cpp: (WebCore::InsertLineBreakCommand::doApply):
+ Use a text node instead of a break element when inserting and the style is preserveNewline.
+
+ * editing/JSEditor.cpp: (WebCore::execRemoveFormat): Pass the selection range
+ to createFragmentFromText.
+
+ * editing/RebalanceWhitespaceCommand.cpp: (WebCore::RebalanceWhitespaceCommand::doApply):
+ Assert that we're in a style that collapses whitespace. It's the caller's responsibility
+ not to call otherwise.
+
+ * editing/ReplaceSelectionCommand.h: Removed unused destructor, type, isSingleTextNode,
+ isTreeFragment, m_type, and added a context parameter to inertFragmentForTestRendering.
+ Also changed the constructor to take a selection rather than a pointer to the root
+ editable element, replaced removeEndBRIfNeeded with shouldRemoveEndBR and removed an
+ unused parameter from shouldMergeEnd.
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplacementFragment::ReplacementFragment): Removed code to set up m_type.
+ Compute root editable element from passed-in selection. Used the start of the selection
+ as a base node for style purposes for the test rendering. Removed the special case
+ "single text node" alternative to createFragmentFromText in the plain text case, since
+ createFragmentFromText now handles that correctly.
+ (WebCore::ReplacementFragment::insertFragmentForTestRendering): Copy the whitespace
+ property from the source location when creating a temporary element for test rendering.
+ (WebCore::ReplacementFragment::shouldMergeEnd): Removed unneeded boolean
+ parameter fragmentHadInterchangeNewlineAtEnd, which is always false.
+ (WebCore::ReplaceSelectionCommand::doApply): Update for ReplacementFragment changes,
+ change code to not remove end BR when it can be re-used instead, don't call the
+ paragraph separator insertion when the position is at the start of a paragraph already,
+ removed redundant computation of identical "next" value, removed unneeded boolean
+ parameter to shouldMergeEnd, add case for merging when all we need to do is to delete
+ a newline character, removed unneeded code to set insertionPos after all code that uses
+ it, and use spaces instead of non-breaking spaces when doing smart paste if the
+ context is one where we do not collapse white space.
+ (WebCore::ReplaceSelectionCommand::shouldRemoveEndBR): Renamed and changed to return
+ a boolean instead of doing the removal.
+
+ * editing/markup.h: Use Vector instead of DeprecatedPtrList. Change the
+ createFragmentFromText function to take a range for context instead of a document.
+ * editing/markup.cpp:
+ (WebCore::markup): Use Vector instead of DeprecatedPtrList.
+ (WebCore::createMarkup): Ditto.
+ (WebCore::createParagraphContentsFromString): Remove unneeded document parameter
+ and changed a couple places to use isEmpty instead of comparing with "".
+ (WebCore::createFragmentFromText): Given the new context parameter, if the context
+ is one that preserves newlines, then use "\n" instead of <br> elements.
+ (WebCore::createFragmentFromNodes): Use Vector instead of DeprecatedPtrList.
+
+ * html/HTMLElement.cpp: (WebCore::HTMLElement::setInnerText): Do not use <br>
+ elements if the context of this node is one where we preserve newlines.
+
+ * rendering/InlineTextBox.h:
+ * rendering/InlineTextBox.cpp: (WebCore::InlineTextBox::containsCaretOffset):
+ Added. Implements the appropriate rule for determining if a caret position is
+ in this line or not. Returns true for both one line and the next in cases where
+ affinity must be considered to determine which line the caret is on.
+
+ * rendering/RenderText.h: Make atLineWrap no longer be a member function.
+ * rendering/RenderText.cpp:
+ (WebCore::atLineWrap): Remove special rule about preserveNewline and isLineBreak,
+ which will no longer apply due to the new containsCaretOffset function logic.
+ (WebCore::RenderText::caretRect): Use containsCaretOffset.
+ (WebCore::RenderText::inlineBox): Ditto.
+
+ * rendering/RenderTextControl.cpp:
+ (WebCore::RenderTextControl::updateFromElement): Make a placeholder <br> element
+ after calling setInnerText so that the last newline in the string has the effect
+ we expect outside the HTML world (an additional line).
+ (WebCore::RenderTextControl::setSelectionRange): Set granularity of the selection
+ too. The layout tests caught this problem, which needs a better solution long term.
+ (WebCore::RenderTextControl::text): Call textContent with the parameter false
+ so it will not include newlines for <br> elements. Now the only <br> element
+ that will ever be in the shadow DOM tree is the one to prevent collapsing, and
+ that one should not show up in the text value.
+
+ * rendering/bidi.cpp: (WebCore::RenderBlock::findNextLineBreak): Took a rule
+ that specifically called out the pre whitespace mode and made it work for all
+ the modes that preserve newlines. This makes sure we get a last line box for
+ text after the last "\n" even in cases where there is no <br> afterward.
+
+ * editing/DeleteSelectionCommand.cpp:
+ (WebCore::DeleteSelectionCommand::fixupWhitespace):
+ * editing/InsertParagraphSeparatorCommand.cpp:
+ (WebCore::InsertParagraphSeparatorCommand::doApply):
+ Added assertions to make sure we don't do anything when we're not collapsing
+ whitespace.
+
+ * html/HTMLTextAreaElement.cpp: (WebCore::HTMLTextAreaElement::setDefaultValue):
+ Changed to use Vector instead of DeprecatedPtrList.
+
+ * editing/HTMLInterchange.cpp: Removed obsolete comment.
+
+ * loader/Cache.h: Removed a stray include.
+
2006-07-24 Adele Peterson <adele@apple.com>
Reviewed by Justin.
#import "config.h"
#import "DOMHTML.h"
+#import "DOMExtensions.h"
#import "DOMHTMLInternal.h"
#import "DOMInternal.h"
#import "DOMPrivate.h"
-#import "DOMExtensions.h"
#import "DocumentFragment.h"
#import "FoundationExtras.h"
+#import "FrameView.h"
#import "HTMLAppletElement.h"
#import "HTMLAreaElement.h"
+#import "HTMLBRElement.h"
#import "HTMLBaseElement.h"
#import "HTMLBaseFontElement.h"
#import "HTMLBodyElement.h"
-#import "HTMLBRElement.h"
#import "HTMLButtonElement.h"
+#import "HTMLDListElement.h"
#import "HTMLDirectoryElement.h"
#import "HTMLDivElement.h"
-#import "HTMLDListElement.h"
#import "HTMLDocument.h"
#import "HTMLEmbedElement.h"
#import "HTMLFieldSetElement.h"
#import "HTMLFormCollection.h"
#import "HTMLFormElement.h"
#import "HTMLFrameSetElement.h"
+#import "HTMLHRElement.h"
#import "HTMLHeadElement.h"
#import "HTMLHeadingElement.h"
-#import "HTMLHRElement.h"
#import "HTMLHtmlElement.h"
#import "HTMLIFrameElement.h"
#import "HTMLImageElement.h"
#import "HTMLIsIndexElement.h"
+#import "HTMLLIElement.h"
#import "HTMLLabelElement.h"
#import "HTMLLegendElement.h"
-#import "HTMLLIElement.h"
#import "HTMLLinkElement.h"
#import "HTMLMapElement.h"
#import "HTMLMenuElement.h"
#import "HTMLMetaElement.h"
#import "HTMLNames.h"
-#import "HTMLObjectElement.h"
#import "HTMLOListElement.h"
+#import "HTMLObjectElement.h"
#import "HTMLOptGroupElement.h"
#import "HTMLOptionElement.h"
#import "HTMLOptionsCollection.h"
-#import "HTMLParamElement.h"
#import "HTMLParagraphElement.h"
+#import "HTMLParamElement.h"
#import "HTMLPreElement.h"
#import "HTMLScriptElement.h"
#import "HTMLSelectElement.h"
#import "HTMLStyleElement.h"
-#import "HTMLTableElement.h"
#import "HTMLTableCaptionElement.h"
#import "HTMLTableCellElement.h"
#import "HTMLTableColElement.h"
+#import "HTMLTableElement.h"
#import "HTMLTableRowElement.h"
#import "HTMLTableSectionElement.h"
#import "HTMLTextAreaElement.h"
#import "HTMLTitleElement.h"
#import "HTMLUListElement.h"
+#import "KURL.h"
#import "NameNodeList.h"
-#import "markup.h"
+#import "Range.h"
#import "RenderTextControl.h"
-#import "FrameView.h"
#import "csshelper.h"
-#import "KURL.h"
+#import "markup.h"
using namespace WebCore;
using namespace HTMLNames;
- (DOMDocumentFragment *)createDocumentFragmentWithText:(NSString *)text
{
- return [DOMDocumentFragment _documentFragmentWith:createFragmentFromText([self _document], DeprecatedString::fromNSString(text)).get()];
+ // FIXME: Since this is not a contextual fragment, it won't handle whitespace properly.
+ return [DOMDocumentFragment _documentFragmentWith:createFragmentFromText([self _document]->createRange().get(), text).get()];
}
@end
- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range;
- (DOMDocumentFragment *)documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString;
-- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text;
+- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text inContext:(DOMRange *)context;
- (DOMDocumentFragment *)documentFragmentWithNodesAsParagraphs:(NSArray *)nodes;
- (void)replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle;
return m_frame->documentTypeString() + markupString;
}
-- (NSArray *)nodesFromList:(DeprecatedPtrList<Node> *)nodeList
+- (NSArray *)nodesFromList:(Vector<Node*> *)nodesVector
{
- NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:nodeList->count()];
- for (DeprecatedPtrListIterator<Node> i(*nodeList); i.current(); ++i)
- [nodes addObject:[DOMNode _nodeWith:i.current()]];
-
+ size_t size = nodesVector->size();
+ NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size];
+ for (size_t i = 0; i < size; ++i)
+ [nodes addObject:[DOMNode _nodeWith:(*nodesVector)[i]]];
return nodes;
}
- (NSString *)markupStringFromNode:(DOMNode *)node nodes:(NSArray **)nodes
{
// FIXME: This is never "for interchange". Is that right? See the next method.
- DeprecatedPtrList<Node> nodeList;
+ Vector<Node*> nodeList;
NSString *markupString = createMarkup([node _node], IncludeNode, nodes ? &nodeList : 0).getNSString();
if (nodes)
*nodes = [self nodesFromList:&nodeList];
- (NSString *)markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes
{
// FIXME: This is always "for interchange". Is that right? See the previous method.
- DeprecatedPtrList<Node> nodeList;
+ Vector<Node*> nodeList;
NSString *markupString = createMarkup([range _range], nodes ? &nodeList : 0, AnnotateForInterchange).getNSString();
if (nodes)
*nodes = [self nodesFromList:&nodeList];
DeprecatedString::fromNSString(markupString), DeprecatedString::fromNSString(baseURLString)).get()];
}
-- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text
+- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text inContext:(DOMRange *)context
{
- if (!m_frame || !m_frame->document() || !text)
- return 0;
-
- return [DOMDocumentFragment _documentFragmentWith:createFragmentFromText(m_frame->document(), DeprecatedString::fromNSString(text)).get()];
+ return [DOMDocumentFragment _documentFragmentWith:createFragmentFromText([context _range], DeprecatedString::fromNSString(text)).get()];
}
- (DOMDocumentFragment *)documentFragmentWithNodesAsParagraphs:(NSArray *)nodes
{
- NSEnumerator *nodeEnum = [nodes objectEnumerator];
- DOMNode *node;
- DeprecatedPtrList<Node> nodeList;
-
if (!m_frame || !m_frame->document())
return 0;
- while ((node = [nodeEnum nextObject])) {
- nodeList.append([node _node]);
- }
+ NSEnumerator *nodeEnum = [nodes objectEnumerator];
+ Vector<Node*> nodesVector;
+ DOMNode *node;
+ while ((node = [nodeEnum nextObject]))
+ nodesVector.append([node _node]);
- return [DOMDocumentFragment _documentFragmentWith:createFragmentFromNodeList(m_frame->document(), nodeList).get()];
+ return [DOMDocumentFragment _documentFragmentWith:createFragmentFromNodes(m_frame->document(), nodesVector).get()];
}
- (void)replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
- (void)replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
{
- [self replaceSelectionWithFragment:[self documentFragmentWithText:text] selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES];
+ [self replaceSelectionWithFragment:[self documentFragmentWithText:text
+ inContext:[DOMRange _rangeWith:m_frame->selection().toRange().get()]]
+ selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES];
}
- (bool)canIncreaseSelectionListLevel
// not rendered. Return false.
return false;
}
- if (offset() >= box->m_start && offset() <= box->m_start + box->m_len)
+ if (box->containsCaretOffset(offset()))
// Return false for offsets inside composed characters.
return offset() == 0 || offset() == textRenderer->nextOffset(textRenderer->previousOffset(offset()));
}
applyCommandToComposite(cmd);
}
-void CompositeEditCommand::rebalanceWhitespaceAt(const Position &position)
+void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
{
+ Node* textNode = position.node();
+ if (!textNode || !textNode->isTextNode())
+ return;
+ if (static_cast<Text*>(textNode)->length() == 0)
+ return;
+ RenderObject* renderer = textNode->renderer();
+ if (renderer && !renderer->style()->collapseWhiteSpace())
+ return;
EditCommandPtr cmd(new RebalanceWhitespaceCommand(document(), position));
applyCommandToComposite(cmd);
}
{
Selection selection = endingSelection();
if (selection.isCaretOrRange()) {
- EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
- applyCommandToComposite(startCmd);
- if (selection.isRange()) {
- EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
- applyCommandToComposite(endCmd);
- }
+ rebalanceWhitespaceAt(endingSelection().start());
+ if (selection.isRange())
+ rebalanceWhitespaceAt(endingSelection().end());
}
}
updateLayout();
// FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore
if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter()) {
- Text *textNode = static_cast<Text *>(m_leadingWhitespace.node());
+ Text* textNode = static_cast<Text*>(m_leadingWhitespace.node());
+ ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
}
if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter()) {
- Text *textNode = static_cast<Text *>(m_trailingWhitespace.node());
+ Text* textNode = static_cast<Text*>(m_trailingWhitespace.node());
+ ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace());
replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
}
}
} // end anonymous namespace
-// FIXME: convertHTMLTextToInterchangeFormat should probably be in the khtml namespace.
// FIXME: Can't really do this work without taking whitespace mode into account.
// This means that eventually this function needs to be eliminated or at least have
// its parameters changed because it can't do its work on the string without knowing
if (selection.isNone())
return;
- RefPtr<Element> breakNode = createBreakElement(document());
- Node* nodeToInsert = breakNode.get();
-
Position pos(selection.start().upstream());
pos = positionAvoidingSpecialElementBoundary(pos);
- if (isTabSpanTextNode(pos.node())) {
- insertNodeAtTabSpanPosition(nodeToInsert, pos);
+ Node* styleNode = pos.node();
+ bool isTabSpan = isTabSpanTextNode(styleNode);
+ if (isTabSpan)
+ styleNode = styleNode->parentNode()->parentNode();
+ RenderObject* styleRenderer = styleNode->renderer();
+ bool useBreakElement = !styleRenderer || !styleRenderer->style()->preserveNewline();
+
+ RefPtr<Node> nodeToInsert;
+ if (useBreakElement)
+ nodeToInsert = createBreakElement(document());
+ else
+ nodeToInsert = document()->createTextNode("\n");
+ // FIXME: Need to merge text nodes when inserting just after or before text.
+
+ if (isTabSpan) {
+ insertNodeAtTabSpanPosition(nodeToInsert.get(), pos);
setEndingSelection(Position(nodeToInsert->traverseNextNode(), 0), DOWNSTREAM);
} else if (isEndOfBlock(VisiblePosition(pos, selection.affinity()))) {
-
Node* block = pos.node()->enclosingBlockFlowElement();
- // Insert an extra br if the inserted one will collapsed because of quirks mode.
- if (!document()->inStrictMode() && !(pos.downstream().node()->hasTagName(brTag) && pos.downstream().offset() == 0)) {
- insertNodeAt(nodeToInsert, pos.node(), pos.offset());
- insertNodeAfter(createBreakElement(document()).get(), nodeToInsert);
- } else
- insertNodeAt(nodeToInsert, pos.node(), pos.offset());
+ // Insert an extra break element so that there will be a blank line after the last
+ // inserted line break. In HTML, a line break at the end of a block ends the last
+ // line in the block, while in editable text, a line break at the end of block
+ // creates a last blank line. We need an extra break element to get HTML to act
+ // the way editable text would.
+ bool haveBreak = pos.downstream().node()->hasTagName(brTag) && pos.downstream().offset() == 0;
+ insertNodeAt(nodeToInsert.get(), pos.node(), pos.offset());
+ if (!haveBreak)
+ insertNodeAfter(createBreakElement(document()).get(), nodeToInsert.get());
setEndingSelection(Position(block, maxDeepOffset(block)), DOWNSTREAM);
- }
- else if (pos.offset() <= pos.node()->caretMinOffset()) {
+ } else if (pos.offset() <= pos.node()->caretMinOffset()) {
LOG(Editing, "input newline case 2");
// Insert node before downstream position, and place caret there as well.
Position endingPosition = pos.downstream();
- insertNodeBeforePosition(nodeToInsert, endingPosition);
+ insertNodeBeforePosition(nodeToInsert.get(), endingPosition);
setEndingSelection(endingPosition, DOWNSTREAM);
} else if (pos.offset() >= pos.node()->caretMaxOffset()) {
LOG(Editing, "input newline case 3");
// Insert BR after this node. Place caret in the position that is downstream
// of the current position, reckoned before inserting the BR in between.
Position endingPosition = pos.downstream();
- insertNodeAfterPosition(nodeToInsert, pos);
+ insertNodeAfterPosition(nodeToInsert.get(), pos);
setEndingSelection(endingPosition, DOWNSTREAM);
} else {
// Split a text node
RefPtr<Text> textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), ec));
deleteTextFromNode(textNode, 0, pos.offset());
insertNodeBefore(textBeforeNode.get(), textNode);
- insertNodeBefore(nodeToInsert, textNode);
+ insertNodeBefore(nodeToInsert.get(), textNode);
Position endingPosition = Position(textNode, 0);
// Handle whitespace that occurs after the split
if (!endingPosition.isRenderedCharacter()) {
// Clear out all whitespace and insert one non-breaking space
deleteInsignificantTextDownstream(endingPosition);
+ ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
insertTextIntoNode(textNode, 0, nonBreakingSpaceString());
}
if (typingStyle && typingStyle->length() > 0) {
Selection selectionBeforeStyle = endingSelection();
- applyStyle(typingStyle, Position(nodeToInsert, 0), Position(nodeToInsert, maxDeepOffset(nodeToInsert)));
+ applyStyle(typingStyle, Position(nodeToInsert.get(), 0),
+ Position(nodeToInsert.get(), maxDeepOffset(nodeToInsert.get())));
setEndingSelection(selectionBeforeStyle);
}
// FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions
// after the preserved newline, causing the newline to be turned into a nbsp.
if (leadingWhitespace.isNotNull()) {
- Text *textNode = static_cast<Text *>(leadingWhitespace.node());
+ Text* textNode = static_cast<Text*>(leadingWhitespace.node());
+ ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
replaceTextInNode(textNode, leadingWhitespace.offset(), 1, nonBreakingSpaceString());
}
pos = Position(startNode, 0);
if (!pos.isRenderedCharacter()) {
// Clear out all whitespace and insert one non-breaking space
- ASSERT(startNode && startNode->isTextNode());
+ ASSERT(startNode);
+ ASSERT(startNode->isTextNode());
+ ASSERT(!startNode->renderer() || startNode->renderer()->style()->collapseWhiteSpace());
deleteInsignificantTextDownstream(pos);
- insertTextIntoNode(static_cast<Text *>(startNode), 0, nonBreakingSpaceString());
+ insertTextIntoNode(static_cast<Text*>(startNode), 0, nonBreakingSpaceString());
}
}
bool execRemoveFormat(Frame* frame, bool userInterface, const String& value)
{
- RefPtr<DocumentFragment> fragment = createFragmentFromText(frame->document(), frame->selection().toString().deprecatedString());
+ RefPtr<DocumentFragment> fragment = createFragmentFromText(frame->selection().toRange().get(), frame->selection().toString());
EditCommandPtr(new ReplaceSelectionCommand(frame->document(), fragment.get(), false, false, false, false, EditActionUnspecified)).apply();
return true;
}
{
if (m_position.isNull() || !m_position.node()->isTextNode())
return;
-
- Text *textNode = static_cast<Text *>(m_position.node());
+
+ Text* textNode = static_cast<Text*>(m_position.node());
String text = textNode->data();
- if (text.length() == 0)
- return;
-
+ ASSERT(!text.isEmpty());
+ ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
+
int offset = m_position.offset();
// If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
if (!isWhitespace(text[offset])) {
}
} // namespace WebCore
-
using namespace HTMLNames;
-ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *fragment, bool matchStyle, Element* editableRoot)
+ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const Selection& selection)
: m_document(document),
m_fragment(fragment),
m_matchStyle(matchStyle),
{
if (!m_document)
return;
-
- if (!m_fragment) {
- m_type = EmptyFragment;
+ if (!m_fragment)
return;
- }
-
Node* firstChild = m_fragment->firstChild();
- Node* lastChild = m_fragment->lastChild();
-
- if (!firstChild) {
- m_type = EmptyFragment;
+ if (!firstChild)
return;
- }
-
- m_type = firstChild == lastChild && firstChild->isTextNode() ? SingleTextNodeFragment : TreeFragment;
+ Element* editableRoot = selection.rootEditableElement();
ASSERT(editableRoot);
if (!editableRoot)
return;
-
- RefPtr<Node> holder = insertFragmentForTestRendering();
+
+ Node* styleNode = selection.base().node();
+ RefPtr<Node> holder = insertFragmentForTestRendering(styleNode);
RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange();
String text = plainText(range.get());
editableRoot->dispatchEvent(evt, ec, true);
ASSERT(ec == 0);
if (text != evt->text() || !editableRoot->isContentRichlyEditable()) {
- // If the root is in plain-text mode, and white-space shouldn't be collapsed, then contruct a fragment that contains one text node
- if (!editableRoot->isContentRichlyEditable() && editableRoot->renderer() && !editableRoot->renderer()->style()->collapseWhiteSpace()) {
- RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
- fragment->appendChild(document->createTextNode(evt->text()), ec);
- ASSERT(ec == 0);
- m_fragment = fragment;
- } else
- m_fragment = createFragmentFromText(document, evt->text().deprecatedString());
- firstChild = m_fragment->firstChild();
- lastChild = m_fragment->firstChild();
-
+ restoreTestRenderingNodesToFragment(holder.get());
removeNode(holder);
- holder = insertFragmentForTestRendering();
+
+ m_fragment = createFragmentFromText(selection.toRange().get(), evt->text());
+ firstChild = m_fragment->firstChild();
+ if (!firstChild)
+ return;
+ holder = insertFragmentForTestRendering(styleNode);
}
-
+
Node *node = firstChild;
Node *newlineAtStartNode = 0;
Node *newlineAtEndNode = 0;
removeStyleNodes();
}
-ReplacementFragment::~ReplacementFragment()
+bool ReplacementFragment::isEmpty() const
{
+ return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd;
}
Node *ReplacementFragment::firstChild() const
ASSERT(ec == 0);
}
-PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering()
+PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering(Node* context)
{
- Node *body = m_document->body();
+ Node* body = m_document->body();
if (!body)
return 0;
- RefPtr<Node> holder = createDefaultParagraphElement(m_document.get());
+ RefPtr<StyledElement> holder = static_pointer_cast<StyledElement>(createDefaultParagraphElement(m_document.get()));
ExceptionCode ec = 0;
+
+ // Copy the whitespace style from the context onto this element.
+ Node* n = context;
+ while (n && !n->isElementNode())
+ n = n->parentNode();
+ if (n) {
+ RefPtr<CSSComputedStyleDeclaration> contextStyle = new CSSComputedStyleDeclaration(static_cast<Element*>(n));
+ CSSStyleDeclaration* style = holder->style();
+ style->setProperty(CSS_PROP_WHITE_SPACE, contextStyle->getPropertyValue(CSS_PROP_WHITE_SPACE), false, ec);
+ ASSERT(ec == 0);
+ }
+
holder->appendChild(m_fragment, ec);
ASSERT(ec == 0);
return false;
}
-bool ReplaceSelectionCommand::shouldMergeEnd(const VisiblePosition& endOfInsertedContent, bool fragmentHadInterchangeNewlineAtEnd, bool selectionEndWasEndOfParagraph)
+bool ReplaceSelectionCommand::shouldMergeEnd(const VisiblePosition& endOfInsertedContent, bool selectionEndWasEndOfParagraph)
{
Node* endNode = endOfInsertedContent.deepEquivalent().node();
Node* nextNode = endOfInsertedContent.next().deepEquivalent().node();
// FIXME: Unify the naming scheme for these enclosing element getters.
return !selectionEndWasEndOfParagraph &&
- !fragmentHadInterchangeNewlineAtEnd &&
isEndOfParagraph(endOfInsertedContent) &&
nearestMailBlockquote(endNode) == nearestMailBlockquote(nextNode) &&
enclosingListChild(endNode) == enclosingListChild(nextNode) &&
m_matchStyle = true;
Element* currentRoot = selection.rootEditableElement();
- ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, currentRoot);
+ ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, selection);
- if (fragment.type() == EmptyFragment)
+ if (fragment.isEmpty())
return;
if (m_matchStyle)
updateLayout();
insertionPos = Position(m_lastNodeInserted.get(), m_lastNodeInserted->caretMaxOffset());
}
+
+ Position lastPositionToSelect;
- removeEndBRIfNeeded(endBR);
+ bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd();
+ bool lastNodeInsertedWasBR = m_lastNodeInserted->hasTagName(brTag);
+
+ if (shouldRemoveEndBR(endBR)) {
+ if (interchangeNewlineAtEnd || lastNodeInsertedWasBR) {
+ interchangeNewlineAtEnd = false;
+ lastNodeInsertedWasBR = false;
+ m_lastNodeInserted = endBR;
+ lastPositionToSelect = VisiblePosition(Position(m_lastNodeInserted.get(), 0)).deepEquivalent();
+ } else
+ removeNodeAndPruneAncestors(endBR);
+ }
// Styles were removed during the test insertion. Restore them.
if (!m_matchStyle)
VisiblePosition endOfInsertedContent(Position(m_lastNodeInserted.get(), maxDeepOffset(m_lastNodeInserted.get())));
VisiblePosition startOfInsertedContent(Position(m_firstNodeInserted.get(), 0));
- Position lastPositionToSelect;
-
- if (fragment.hasInterchangeNewlineAtEnd()) {
+ if (interchangeNewlineAtEnd) {
VisiblePosition pos(insertionPos);
VisiblePosition next = pos.next();
-
- if (endWasEndOfParagraph || !isEndOfParagraph(pos) || next.isNull() || next.rootEditableElement() != currentRoot) {
- setEndingSelection(insertionPos, DOWNSTREAM);
- insertParagraphSeparator();
- // Select up to the paragraph separator that was added.
- lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
- updateNodesInserted(lastPositionToSelect.node());
+ if (endWasEndOfParagraph || !isEndOfParagraph(pos) || next.rootEditableElement() != currentRoot) {
+ if (!isStartOfParagraph(pos)) {
+ setEndingSelection(pos.deepEquivalent().downstream(), DOWNSTREAM);
+ insertParagraphSeparator();
+
+ // Select up to the paragraph separator that was added.
+ lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
+ updateNodesInserted(lastPositionToSelect.node());
+ }
} else {
// Select up to the beginning of the next paragraph.
- VisiblePosition next = pos.next();
lastPositionToSelect = next.deepEquivalent().downstream();
}
-
- } else if (m_lastNodeInserted->hasTagName(brTag)) {
+
+ } else if (lastNodeInsertedWasBR) {
// We want to honor the last incoming line break, so, if it will collapse away because of quirks mode,
// add an extra one.
// FIXME: This will expand a br inside a block: <div><br></div>
if (!document()->inStrictMode() && isEndOfBlock(VisiblePosition(Position(m_lastNodeInserted.get(), 0))))
insertNodeBeforeAndUpdateNodesInserted(createBreakElement(document()).get(), m_lastNodeInserted.get());
- } else if (shouldMergeEnd(endOfInsertedContent, fragment.hasInterchangeNewlineAtEnd(), endWasEndOfParagraph)) {
+ } else if (shouldMergeEnd(endOfInsertedContent, endWasEndOfParagraph)) {
// Make sure that content after the end of the selection being pasted into is in the same paragraph as the
// last bit of content that was inserted.
- // Merging two paragraphs will destroy the moved one's block styles. Always move forward to preserve
- // the block style of the paragraph already in the document, unless the paragraph to move would include the
- // what was the start of the selection that was pasted into.
- bool mergeForward = !inSameParagraph(startOfInsertedContent, endOfInsertedContent);
-
- VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent;
- VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next();
-
- moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
- // Merging forward will remove m_lastNodeInserted from the document.
- // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are
- // only ever used to create positions where inserted content starts/ends.
- if (mergeForward)
- m_lastNodeInserted = destination.previous().deepEquivalent().node();
+ Position downstreamEnd = endOfInsertedContent.deepEquivalent().downstream();
+ Position upstreamOnePastEnd = endOfInsertedContent.next().deepEquivalent().upstream();
+ Node* node = downstreamEnd.node();
+ int offset = downstreamEnd.offset();
+ if (node && node == upstreamOnePastEnd.node() && offset + 1 == upstreamOnePastEnd.offset() && node->isTextNode()) {
+ // Special case for removing a newline character.
+ Text* textNode = static_cast<Text*>(node);
+ if (textNode->length() == 1)
+ removeNodeAndPruneAncestors(textNode);
+ else
+ deleteTextFromNode(textNode, offset, 1);
+ } else {
+ // Merging two paragraphs will destroy the moved one's block styles. Always move forward to preserve
+ // the block style of the paragraph already in the document, unless the paragraph to move would include the
+ // what was the start of the selection that was pasted into.
+ bool mergeForward = !inSameParagraph(startOfInsertedContent, endOfInsertedContent);
+
+ VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent;
+ VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next();
+
+ moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
+ // Merging forward will remove m_lastNodeInserted from the document.
+ // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are
+ // only ever used to create positions where inserted content starts/ends.
+ if (mergeForward)
+ m_lastNodeInserted = destination.previous().deepEquivalent().node();
+ }
}
endOfInsertedContent = VisiblePosition(Position(m_lastNodeInserted.get(), maxDeepOffset(m_lastNodeInserted.get())));
bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) &&
!frame->isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false);
if (needsTrailingSpace) {
+ RenderObject* renderer = m_lastNodeInserted->renderer();
+ bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
if (m_lastNodeInserted->isTextNode()) {
- Text *text = static_cast<Text *>(m_lastNodeInserted.get());
- insertTextIntoNode(text, text->length(), nonBreakingSpaceString());
- insertionPos = Position(text, text->length());
+ Text* text = static_cast<Text*>(m_lastNodeInserted.get());
+ insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " ");
} else {
- RefPtr<Node> node = document()->createEditingTextNode(nonBreakingSpaceString());
+ RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
insertNodeAfterAndUpdateNodesInserted(node.get(), m_lastNodeInserted.get());
- insertionPos = Position(node.get(), 1);
}
}
bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) &&
!frame->isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true);
if (needsLeadingSpace) {
+ RenderObject* renderer = m_lastNodeInserted->renderer();
+ bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
if (m_firstNodeInserted->isTextNode()) {
- Text *text = static_cast<Text *>(m_firstNodeInserted.get());
- insertTextIntoNode(text, 0, nonBreakingSpaceString());
+ Text* text = static_cast<Text*>(m_firstNodeInserted.get());
+ insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
} else {
- RefPtr<Node> node = document()->createEditingTextNode(nonBreakingSpaceString());
+ RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
// Don't updateNodesInserted. Doing so would set m_lastNodeInserted to be the node containing the
// leading space, but m_lastNodeInserted is supposed to mark the end of pasted content.
insertNodeBefore(node.get(), m_firstNodeInserted.get());
completeHTMLReplacement(lastPositionToSelect);
}
-void ReplaceSelectionCommand::removeEndBRIfNeeded(Node* endBR)
+bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR)
{
if (!endBR || !endBR->inDocument())
- return;
+ return false;
VisiblePosition visiblePos(Position(endBR, 0));
- if (// The br is collapsed away and so is unnecessary.
+ return
+ // The br is collapsed away and so is unnecessary.
!document()->inStrictMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos) ||
// A br that was originally holding a line open should be displaced by inserted content.
// A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
- isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos))
- removeNodeAndPruneAncestors(endBR);
+ isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);
}
void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect)
// --- ReplacementFragment helper class
-class ReplacementFragment
+class ReplacementFragment : Noncopyable
{
public:
- ReplacementFragment(Document*, DocumentFragment*, bool, Element*);
- ~ReplacementFragment();
+ ReplacementFragment(Document*, DocumentFragment*, bool matchStyle, const Selection&);
- Node *firstChild() const;
- Node *lastChild() const;
+ Node* firstChild() const;
+ Node* lastChild() const;
- Node *mergeStartNode() const;
+ Node* mergeStartNode() const;
const RenderingInfoMap& renderingInfo() const { return m_renderingInfo; }
const NodeVector& nodes() const { return m_nodes; }
- EFragmentType type() const { return m_type; }
- bool isEmpty() const { return m_type == EmptyFragment; }
- bool isSingleTextNode() const { return m_type == SingleTextNodeFragment; }
- bool isTreeFragment() const { return m_type == TreeFragment; }
+ bool isEmpty() const;
bool hasMoreThanOneBlock() const { return m_hasMoreThanOneBlock; }
bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAtStart; }
void removeNode(PassRefPtr<Node>);
-private:
- // no copy construction or assignment
- ReplacementFragment(const ReplacementFragment &);
- ReplacementFragment &operator=(const ReplacementFragment &);
-
- static bool isInterchangeNewlineNode(const Node *);
- static bool isInterchangeConvertedSpaceSpan(const Node *);
+private:
+ static bool isInterchangeNewlineNode(const Node*);
+ static bool isInterchangeConvertedSpaceSpan(const Node*);
- PassRefPtr<Node> insertFragmentForTestRendering();
+ PassRefPtr<Node> insertFragmentForTestRendering(Node* context);
void saveRenderingInfo(Node*);
void computeStylesUsingTestRendering(Node*);
void removeUnrenderedNodes(Node*);
int renderedBlocks(Node*);
void removeStyleNodes();
- Node *enclosingBlock(Node *) const;
- void removeNodePreservingChildren(Node *);
- void insertNodeBefore(Node *node, Node *refNode);
+ Node* enclosingBlock(Node*) const;
+ void removeNodePreservingChildren(Node*);
+ void insertNodeBefore(Node* node, Node* refNode);
- EFragmentType m_type;
RefPtr<Document> m_document;
RefPtr<DocumentFragment> m_fragment;
RenderingInfoMap m_renderingInfo;
void updateNodesInserted(Node *);
void fixupNodeStyles(const NodeVector&, const RenderingInfoMap&);
- void removeEndBRIfNeeded(Node*);
+ bool shouldRemoveEndBR(Node*);
bool shouldMergeStart(const ReplacementFragment&, const Selection&);
- bool shouldMergeEnd(const VisiblePosition&, bool, bool);
+ bool shouldMergeEnd(const VisiblePosition&, bool selectionEndWasEndOfParagraph);
RefPtr<Node> m_firstNodeInserted;
RefPtr<Node> m_lastNodeInserted;
return "";
}
-static DeprecatedString markup(const Node *startNode, bool onlyIncludeChildren, bool includeSiblings, DeprecatedPtrList<Node> *nodes)
+static DeprecatedString markup(Node* startNode, bool onlyIncludeChildren, bool includeSiblings, Vector<Node*> *nodes)
{
// Doesn't make sense to only include children and include siblings.
ASSERT(!(onlyIncludeChildren && includeSiblings));
DeprecatedString me = "";
- for (const Node *current = startNode; current != NULL; current = includeSiblings ? current->nextSibling() : NULL) {
+ for (Node* current = startNode; current != NULL; current = includeSiblings ? current->nextSibling() : NULL) {
if (!onlyIncludeChildren) {
if (nodes)
nodes->append(current);
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
// FIXME: At least, annotation and style info should probably not be included in range.markupString()
-DeprecatedString createMarkup(const Range *range, DeprecatedPtrList<Node> *nodes, EAnnotateForInterchange annotate)
+DeprecatedString createMarkup(const Range *range, Vector<Node*>* nodes, EAnnotateForInterchange annotate)
{
if (!range || range->isDetached())
return DeprecatedString();
DeprecatedStringList markups;
Node *pastEnd = range->pastEndNode();
Node *lastClosed = 0;
- DeprecatedPtrList<Node> ancestorsToClose;
+ Vector<Node*> ancestorsToClose;
// calculate the "default style" for this markup
Position pos(doc->documentElement(), 0);
if (n->nextSibling() == 0 || next == pastEnd) {
if (!ancestorsToClose.isEmpty()) {
// Close up the ancestors.
- while (Node *ancestor = ancestorsToClose.last()) {
+ do {
+ Node *ancestor = ancestorsToClose.last();
if (next != pastEnd && next->isAncestor(ancestor))
break;
// Not at the end of the range, close ancestors up to sibling of next node.
markups.append(endMarkup(ancestor));
lastClosed = ancestor;
ancestorsToClose.removeLast();
- }
+ } while (!ancestorsToClose.isEmpty());
} else {
if (next != pastEnd) {
Node *nextParent = next->parentNode();
return fragment.release();
}
-DeprecatedString createMarkup(const Node *node, EChildrenOnly includeChildren,
- DeprecatedPtrList<Node> *nodes, EAnnotateForInterchange annotate)
+DeprecatedString createMarkup(const Node* node, EChildrenOnly includeChildren,
+ Vector<Node*>* nodes, EAnnotateForInterchange annotate)
{
ASSERT(annotate == DoNotAnnotateForInterchange); // annotation not yet implemented for this code path
node->document()->updateLayoutIgnorePendingStylesheets();
- return markup(node, includeChildren, false, nodes);
+ return markup(const_cast<Node*>(node), includeChildren, false, nodes);
}
-static void createParagraphContentsFromString(Document *document, Element *paragraph, const DeprecatedString &string)
+static void createParagraphContentsFromString(Element* paragraph, const DeprecatedString& string)
{
+ Document* document = paragraph->document();
+
ExceptionCode ec = 0;
if (string.isEmpty()) {
paragraph->appendChild(createBlockPlaceholderElement(document), ec);
// append the non-tab textual part
if (!s.isEmpty()) {
- if (tabText != "") {
+ if (!tabText.isEmpty()) {
paragraph->appendChild(createTabSpanElement(document, tabText), ec);
ASSERT(ec == 0);
tabText = "";
// there is a tab after every entry, except the last entry
// (if the last character is a tab, the list gets an extra empty entry)
- if (!tabList.isEmpty()) {
+ if (!tabList.isEmpty())
tabText += '\t';
- } else if (tabText != "") {
+ else if (!tabText.isEmpty()) {
paragraph->appendChild(createTabSpanElement(document, tabText), ec);
ASSERT(ec == 0);
}
}
}
-PassRefPtr<DocumentFragment> createFragmentFromText(Document *document, const DeprecatedString &text)
+PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
{
- if (!document)
+ if (!context)
return 0;
+ Node* styleNode = context->startNode();
+ if (!styleNode) {
+ styleNode = context->startPosition().node();
+ if (!styleNode)
+ return 0;
+ }
+
+ Document* document = styleNode->document();
RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
- if (!text.isEmpty()) {
- DeprecatedString string = text;
-
- // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
- string.replace("\r\n", "\n");
- string.replace('\r', '\n');
- DeprecatedStringList list = DeprecatedStringList::split('\n', string, true); // true gets us empty strings in the list
- while (!list.isEmpty()) {
- DeprecatedString s = list.first();
- list.pop_front();
-
- ExceptionCode ec = 0;
+ if (text.isEmpty())
+ return fragment.release();
+
+ DeprecatedString string = text.deprecatedString();
+ string.replace("\r\n", "\n");
+ string.replace('\r', '\n');
+
+ ExceptionCode ec = 0;
+ RenderObject* renderer = styleNode->renderer();
+ if (renderer && renderer->style()->preserveNewline()) {
+ fragment->appendChild(document->createTextNode(string), ec);
+ ASSERT(ec == 0);
+ if (string.endsWith("\n")) {
RefPtr<Element> element;
- if (s.isEmpty() && list.isEmpty()) {
- // For last line, use the "magic BR" rather than a P.
- element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
- ASSERT(ec == 0);
- element->setAttribute(classAttr, AppleInterchangeNewline);
- } else {
- element = createDefaultParagraphElement(document);
- createParagraphContentsFromString(document, element.get(), s);
- }
- fragment->appendChild(element.get(), ec);
+ element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
+ ASSERT(ec == 0);
+ element->setAttribute(classAttr, AppleInterchangeNewline);
+ fragment->appendChild(element.release(), ec);
ASSERT(ec == 0);
}
+ return fragment.release();
+ }
+
+ // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
+ DeprecatedStringList list = DeprecatedStringList::split('\n', string, true); // true gets us empty strings in the list
+ while (!list.isEmpty()) {
+ DeprecatedString s = list.first();
+ list.pop_front();
+
+ RefPtr<Element> element;
+ if (s.isEmpty() && list.isEmpty()) {
+ // For last line, use the "magic BR" rather than a P.
+ element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
+ ASSERT(ec == 0);
+ element->setAttribute(classAttr, AppleInterchangeNewline);
+ } else {
+ element = createDefaultParagraphElement(document);
+ createParagraphContentsFromString(element.get(), s);
+ }
+ fragment->appendChild(element.release(), ec);
+ ASSERT(ec == 0);
}
return fragment.release();
}
-PassRefPtr<DocumentFragment> createFragmentFromNodeList(Document *document, const DeprecatedPtrList<Node> &nodeList)
+PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
{
if (!document)
return 0;
RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
ExceptionCode ec = 0;
- for (DeprecatedPtrListIterator<Node> i(nodeList); i.current(); ++i) {
+ size_t size = nodes.size();
+ for (size_t i = 0; i < size; ++i) {
RefPtr<Element> element = createDefaultParagraphElement(document);
- element->appendChild(i.current(), ec);
+ element->appendChild(nodes[i], ec);
ASSERT(ec == 0);
fragment->appendChild(element.release(), ec);
ASSERT(ec == 0);
#include "HTMLInterchange.h"
#include <wtf/Forward.h>
+#include <wtf/Vector.h>
namespace WebCore {
class Range;
class String;
- template <class T> class DeprecatedPtrList;
-
enum EChildrenOnly { IncludeNode, ChildrenOnly };
- PassRefPtr<DocumentFragment> createFragmentFromText(Document*, const DeprecatedString &text);
- PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String &markup, const String &baseURL);
- PassRefPtr<DocumentFragment> createFragmentFromNodeList(Document*, const DeprecatedPtrList<Node> &nodeList);
+ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text);
+ PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL);
+ PassRefPtr<DocumentFragment> createFragmentFromNodes(Document*, const Vector<Node*>&);
- DeprecatedString createMarkup(const Range *range,
- DeprecatedPtrList<Node> *nodes = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange);
- DeprecatedString createMarkup(const Node *node, EChildrenOnly = IncludeNode,
- DeprecatedPtrList<Node> *nodes = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange);
+ DeprecatedString createMarkup(const Range*,
+ Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange);
+ DeprecatedString createMarkup(const Node*, EChildrenOnly = IncludeNode,
+ Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange);
}
return;
}
+ // FIXME: Add special case for when we had one text child and we just want to set its text?
+ // FIXME: Add special case for cases where we can use replaceChild?
+
removeChildren();
appendChild(fragment.release(), ec);
}
return;
}
- // FIXME: Why doesn't this have code to merge neighboring text nodes the
- // way setOuterText does?
+ // FIXME: Add special case for when we had one text child and we just want to set its text?
+ // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does?
parent->replaceChild(fragment.release(), this, ec);
}
return;
}
+ // FIXME: This doesn't take whitespace collapsing into account at all.
+ // FIXME: Add special case for when we had one text child and we just want to set its text?
+ // FIXME: Add special case for cases where we can use replaceChild?
+
removeChildren();
+ if (!text.contains('\n') && !text.contains('\r')) {
+ if (text.isEmpty())
+ return;
+ appendChild(new Text(document(), text), ec);
+ return;
+ }
+
+ // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
+ // FIXME: Can the renderer be out of date here? Do we need to call updateRendering?
+ // For example, for the contents of textarea elements that are display:none?
+ RenderObject* r = renderer();
+ if (r && r->style()->preserveNewline()) {
+ if (!text.contains('\r')) {
+ appendChild(new Text(document(), text), ec);
+ return;
+ }
+ // FIXME: Stick with String once it has a replace that can do this.
+ DeprecatedString textWithConsistentLineBreaks = text.deprecatedString();
+ textWithConsistentLineBreaks.replace("\r\n", "\n");
+ textWithConsistentLineBreaks.replace('\r', '\n');
+ appendChild(new Text(document(), textWithConsistentLineBreaks), ec);
+ return;
+ }
+
// Add text nodes and <br> elements.
+ ec = 0;
+ int lineStart = 0;
+ UChar prev = 0;
int length = text.length();
- if (!text.contains('\n') && !text.contains('\r') && length)
- appendChild(new Text(document(), text), ec);
- else {
- ec = 0;
- int lineStart = 0;
- UChar prev = 0;
- for (int i = 0; i < length; ++i) {
- UChar c = text[i];
- if (c == '\n' || c == '\r') {
- if (i > lineStart) {
- appendChild(new Text(document(), text.substring(lineStart, i - lineStart)), ec);
- if (ec)
- return;
- }
- if (!(c == '\n' && i != 0 && prev == '\r')) {
- appendChild(new HTMLBRElement(document()), ec);
- if (ec)
- return;
- }
- lineStart = i + 1;
+ for (int i = 0; i < length; ++i) {
+ UChar c = text[i];
+ if (c == '\n' || c == '\r') {
+ if (i > lineStart) {
+ appendChild(new Text(document(), text.substring(lineStart, i - lineStart)), ec);
+ if (ec)
+ return;
+ }
+ if (!(c == '\n' && i != 0 && prev == '\r')) {
+ appendChild(new HTMLBRElement(document()), ec);
+ if (ec)
+ return;
}
- prev = c;
+ lineStart = i + 1;
}
- if (length > lineStart)
- appendChild(new Text(document(), text.substring(lineStart, length - lineStart)), ec);
+ prev = c;
}
+ if (length > lineStart)
+ appendChild(new Text(document(), text.substring(lineStart, length - lineStart)), ec);
}
void HTMLElement::setOuterText(const String &text, ExceptionCode& ec)
void HTMLTextAreaElement::setValue(const String& value)
{
- DeprecatedString string = value.deprecatedString();
// WebCoreTextArea normalizes line endings added by the user via the keyboard or pasting.
// We must normalize line endings coming from JS.
- string.replace("\r\n", "\n");
- string.replace("\r", "\n");
+ DeprecatedString valueWithNormalizedLineEndings = value.deprecatedString();
+ valueWithNormalizedLineEndings.replace("\r\n", "\n");
+ valueWithNormalizedLineEndings.replace("\r", "\n");
- m_value = String(string);
+ m_value = valueWithNormalizedLineEndings;
setValueMatchesRenderer();
if (renderer())
renderer()->updateFromElement();
{
String val = "";
- // there may be comments - just grab the text nodes
+ // Since there may be comments, ignore nodes other than text nodes.
for (Node* n = firstChild(); n; n = n->nextSibling())
if (n->isTextNode())
val += static_cast<Text*>(n)->data();
// FIXME: We should only drop the first carriage return for the default
- // value in the original source, not defaultValues set from JS.
+ // value in the original source, not defaultValues set from JS. This code
+ // will do both.
if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n')
val.remove(0, 2);
else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n'))
void HTMLTextAreaElement::setDefaultValue(const String& defaultValue)
{
- // there may be comments - remove all the text nodes and replace them with one
- DeprecatedPtrList<Node> toRemove;
- Node *n;
- for (n = firstChild(); n; n = n->nextSibling())
+ // To preserve comments, remove all the text nodes, then add a single one.
+ Vector<RefPtr<Node> > textNodes;
+ for (Node* n = firstChild(); n; n = n->nextSibling())
if (n->isTextNode())
- toRemove.append(n);
- DeprecatedPtrListIterator<Node> it(toRemove);
+ textNodes.append(n);
ExceptionCode ec = 0;
- for (; it.current(); ++it) {
- RefPtr<Node> n = it.current();
- removeChild(n.get(), ec);
- }
+ size_t size = textNodes.size();
+ for (size_t i = 0; i < size; ++i)
+ removeChild(textNodes[i].get(), ec);
insertBefore(document()->createTextNode(defaultValue), firstChild(), ec);
setValue(defaultValue);
}
#define Cache_h
#include "CachePolicy.h"
-#include "DeprecatedPtrList.h"
#include "PlatformString.h"
#include <wtf/HashSet.h>
IntPoint(m_x, 0), 0)).right();
}
+bool InlineTextBox::containsCaretOffset(int offset) const
+{
+ // Offsets before the box are never "in".
+ if (offset < m_start)
+ return false;
+
+ int pastEnd = m_start + m_len;
+
+ // Offsets inside the box (not at either edge) are always "in".
+ if (offset < pastEnd)
+ return true;
+
+ // Offsets outside the box are always "out".
+ if (offset > pastEnd)
+ return false;
+
+ // Offsets at the end are "out" for line breaks (they are on the next line).
+ if (isLineBreak())
+ return false;
+
+ // Offsets at the end are "in" for normal boxes (but the caller has to check affinity).
+ return true;
+}
+
}
int textPos() const;
int offsetForPosition(int _x, bool includePartialGlyphs = true) const;
int positionForOffset(int offset) const;
+
+ bool containsCaretOffset(int offset) const; // false for offset after line break
int m_start;
unsigned short m_len;
return lastChild->object();
}
-bool RenderText::atLineWrap(InlineTextBox *box, int offset)
+static inline bool atLineWrap(InlineTextBox* box, int offset)
{
- if (box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len) {
- if (!style()->preserveNewline() || !box->isLineBreak())
- return true;
- }
-
- return false;
+ return box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len;
}
IntRect RenderText::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
// Find the text box for the given offset
InlineTextBox *box = 0;
for (box = firstTextBox(); box; box = box->nextTextBox()) {
- if ((offset >= box->m_start) && (offset <= box->m_start + box->m_len)) {
+ if (box->containsCaretOffset(offset)) {
// Check if downstream affinity would make us move to the next line.
if (atLineWrap(box, offset) && affinity == DOWNSTREAM) {
// Use the next text box
return l;
}
-InlineBox *RenderText::inlineBox(int offset, EAffinity affinity)
+InlineBox* RenderText::inlineBox(int offset, EAffinity affinity)
{
- for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
- if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ if (box->containsCaretOffset(offset)) {
if (atLineWrap(box, offset) && affinity == DOWNSTREAM)
return box->nextTextBox();
return box;
}
-
- if (offset < box->m_start) {
+ if (offset < box->m_start)
// The offset we're looking for is before this node
// this means the offset must be in content that is
// not rendered.
return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
- }
}
return 0;
virtual int caretMaxOffset() const;
virtual unsigned caretMaxRenderedOffset() const;
- virtual int previousOffset (int current) const;
- virtual int nextOffset (int current) const;
+ virtual int previousOffset(int current) const;
+ virtual int nextOffset(int current) const;
- bool atLineWrap(InlineTextBox *box, int offset);
- bool containsReversedText() { return m_containsReversedText; }
+ bool containsReversedText() const { return m_containsReversedText; }
public:
InlineTextBox * findNextInlineTextBox( int offset, int &pos ) const;
#include "Event.h"
#include "EventNames.h"
#include "Frame.h"
+#include "HTMLBRElement.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLTextAreaElement.h"
if (value != oldText || !m_div->hasChildNodes()) {
ExceptionCode ec = 0;
m_div->setInnerText(value, ec);
+ if (value.endsWith("\n") || value.endsWith("\r"))
+ m_div->appendChild(new HTMLBRElement(document()), ec);
if (document()->frame())
document()->frame()->clearUndoRedoOperations();
setEdited(false);
SelectionController sel = SelectionController(startPosition, endPosition);
document()->frame()->setSelection(sel);
+ // FIXME: Granularity is stored separately on the frame, but also in the selection controller.
+ // The granularity in the selection controller should be used, and then this line of code would not be needed.
+ document()->frame()->setSelectionGranularity(CharacterGranularity);
}
VisiblePosition RenderTextControl::visiblePositionForIndex(int index)
String RenderTextControl::text()
{
if (m_div)
- return m_div->textContent(true).replace('\\', backslashAsCurrencySymbol());
+ return m_div->textContent().replace('\\', backslashAsCurrencySymbol());
return String();
}
if (lBreak == start && !lBreak.obj->isBR()) {
// we just add as much as possible
- if (style()->whiteSpace() == PRE) {
+ if (style()->preserveNewline()) {
// FIXME: Don't really understand this case.
if (pos != 0) {
lBreak.obj = o;
+2006-07-24 Darin Adler <darin@apple.com>
+
+ Reviewed by Adele and Justin.
+
+ - update for change to require context when creating fragments from text
+ (needed to handle whitespace properly)
+
+ * WebView/WebHTMLView.m:
+ (-[WebHTMLView _documentFragmentFromPasteboard:inContext:allowPlainText:chosePlainText:]):
+ Added context parameter, pass through to bridge.
+ (-[WebHTMLView _pasteWithPasteboard:allowPlainText:]): Pass selection range as context
+ when calling above method.
+ (-[WebHTMLView concludeDragForDraggingInfo:actionMask:]): Pass drag caret as context when
+ calling above method.
+
2006-07-24 Maciej Stachowiak <mjs@apple.com>
Reviewed by Geoff.
@interface WebHTMLView (WebHTMLViewFileInternal)
- (BOOL)_imageExistsAtPaths:(NSArray *)paths;
-- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText;
+- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText;
- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
return elements;
}
-- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText
+- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
+ inContext:(DOMRange *)context
+ allowPlainText:(BOOL)allowPlainText
+ chosePlainText:(BOOL *)chosePlainText
{
NSArray *types = [pasteboard types];
*chosePlainText = NO;
if (allowPlainText && [types containsObject:NSStringPboardType]) {
*chosePlainText = YES;
- return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]];
+ return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
+ inContext:context];
}
return nil;
- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
{
+ DOMRange *range = [self _selectedRange];
BOOL chosePlainText;
- DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard allowPlainText:allowPlainText chosePlainText:&chosePlainText];
+ DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
+ inContext:range allowPlainText:allowPlainText chosePlainText:&chosePlainText];
WebFrameBridge *bridge = [self _bridge];
if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
[bridge replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:chosePlainText];
if ([self _canProcessDragWithDraggingInfo:draggingInfo]) {
NSPasteboard *pasteboard = [draggingInfo draggingPasteboard];
if ([self _isMoveDrag] || [innerBridge isDragCaretRichlyEditable]) {
+ DOMRange *range = [innerBridge dragCaretDOMRange];
BOOL chosePlainText;
- DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard allowPlainText:YES chosePlainText:&chosePlainText];
- if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[innerBridge dragCaretDOMRange] givenAction:WebViewInsertActionDropped]) {
+ DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
+ inContext:range allowPlainText:YES chosePlainText:&chosePlainText];
+ if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionDropped]) {
[[webView _UIDelegateForwarder] webView:webView willPerformDragDestinationAction:WebDragDestinationActionEdit forDraggingInfo:draggingInfo];
if ([self _isMoveDrag]) {
BOOL smartMove = [innerBridge selectionGranularity] == WebBridgeSelectByWord && [self _canSmartReplaceWithPasteboard:pasteboard];