Commit 4ac2b9a3 authored by justing's avatar justing
Browse files

LayoutTests:

        Reviewed by darin
        
        <rdar://problem/5046875> 
        Gmail Editor: Applying alignment to selected text in message also applies alignment to signature

        Added:
        * editing/style/5046875-1-expected.checksum: Added.
        * editing/style/5046875-1-expected.png: Added.
        * editing/style/5046875-1-expected.txt: Added.
        * editing/style/5046875-1.html: Added.
        * editing/style/5046875-2-expected.checksum: Added.
        * editing/style/5046875-2-expected.png: Added.
        * editing/style/5046875-2-expected.txt: Added.
        * editing/style/5046875-2.html: Added.
        moveParagraphs doesn't move unnecessary brs:
        * editing/style/create-block-for-style-002-expected.txt:
        * editing/style/create-block-for-style-004-expected.txt:
        * editing/style/create-block-for-style-001-expected.txt:
        * editing/style/create-block-for-style-009-expected.txt:
        * editing/style/create-block-for-style-010-expected.txt:
        moveParagraphs clears out unrendered text during the move:
        * editing/style/create-block-for-style-011-expected.txt:
        * editing/style/create-block-for-style-007-expected.txt:
        * editing/style/create-block-for-style-012-expected.txt:

WebCore:

        Reviewed by darin
        
        <rdar://problem/5046875> 
        Gmail Editor: Applying alignment to selected text in message also applies alignment to signature

        * editing/ApplyStyleCommand.cpp:
        (WebCore::ApplyStyleCommand::doApply): Don't call applyBlockStyle unless
        there is a block style to apply.
        (WebCore::ApplyStyleCommand::applyBlockStyle): Don't do the remove step.
        It was unnecessary and removed properties from blocks that could contain 
        content outside the range being operated on (added a testcase).
        (WebCore::ApplyStyleCommand::addBlockStyleIfNeeded): Used an early return
        instead of if-nesting.
        * editing/ApplyStyleCommand.h:
        * editing/CompositeEditCommand.cpp:
        (WebCore::CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary):
        Return the new block, if one was created.  Use moveParagraphs to move
        paragraphs into the new block, instead of moving nodes.  The old code moved
        too much (added a testcase).
        * editing/CompositeEditCommand.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@20181 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a258f43a
2007-03-13 Justin Garcia <justin.garcia@apple.com>
Reviewed by darin
<rdar://problem/5046875>
Gmail Editor: Applying alignment to selected text in message also applies alignment to signature
Added:
* editing/style/5046875-1-expected.checksum: Added.
* editing/style/5046875-1-expected.png: Added.
* editing/style/5046875-1-expected.txt: Added.
* editing/style/5046875-1.html: Added.
* editing/style/5046875-2-expected.checksum: Added.
* editing/style/5046875-2-expected.png: Added.
* editing/style/5046875-2-expected.txt: Added.
* editing/style/5046875-2.html: Added.
moveParagraphs doesn't move unnecessary brs:
* editing/style/create-block-for-style-002-expected.txt:
* editing/style/create-block-for-style-004-expected.txt:
* editing/style/create-block-for-style-001-expected.txt:
* editing/style/create-block-for-style-009-expected.txt:
* editing/style/create-block-for-style-010-expected.txt:
moveParagraphs clears out unrendered text during the move:
* editing/style/create-block-for-style-011-expected.txt:
* editing/style/create-block-for-style-007-expected.txt:
* editing/style/create-block-for-style-012-expected.txt:
2007-03-13 Oliver Hunt <oliver@apple.com>
 
Reviewed by Brady.
a94230806bbd60be4d44da44675ced89
\ No newline at end of file
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 781x18
text run at (0,0) width 476: "This tests for a bug where centering two paragraphs would center the third. "
text run at (476,0) width 305: "Only the selected paragraphs should be selected."
RenderBlock {DIV} at (0,34) size 784x54
RenderBlock {DIV} at (0,0) size 784x18
RenderText {#text} at (381,0) size 21x18
text run at (381,0) width 21: "foo"
RenderBlock (anonymous) at (0,18) size 784x0
RenderBlock {DIV} at (0,18) size 784x36
RenderBlock {DIV} at (0,0) size 784x18
RenderText {#text} at (382,0) size 20x18
text run at (382,0) width 20: "bar"
RenderBlock (anonymous) at (0,18) size 784x18
RenderText {#text} at (0,0) size 22x18
text run at (0,0) width 22: "baz"
selection start: position 0 of child 0 {#text} of child 0 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
selection end: position 3 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
<p>This tests for a bug where centering two paragraphs would center the third. Only the selected paragraphs should be selected.</p>
<div id="div" contenteditable="true">foo<div>bar<br>baz</div>
<script>
var div = document.getElementById("div");
var sel = window.getSelection();
sel.setPosition(div, 0);
sel.modify("extend", "forward", "word");
sel.modify("extend", "forward", "paragraph");
document.execCommand("JustifyCenter");
</script>
\ No newline at end of file
84077593fe6858d8d3d047f51b9b618b
\ No newline at end of file
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 784x36
RenderText {#text} at (0,0) size 762x36
text run at (0,0) width 730: "This tests for a bug where left justifying a paragraph amongst several that are centered would left justify all of them. "
text run at (730,0) width 32: "Only"
text run at (0,18) width 281: "the second paragraph should be left justified."
RenderBlock {DIV} at (0,52) size 784x54
RenderBlock (anonymous) at (0,0) size 784x18
RenderText {#text} at (381,0) size 21x18
text run at (381,0) width 21: "foo"
RenderBR {BR} at (402,14) size 0x0
RenderBlock {DIV} at (0,18) size 784x18
RenderText {#text} at (0,0) size 20x18
text run at (0,0) width 20: "bar"
RenderBlock (anonymous) at (0,36) size 784x0
RenderBlock {DIV} at (0,36) size 784x18
RenderText {#text} at (381,0) size 22x18
text run at (381,0) width 22: "baz"
selection start: position 0 of child 0 {#text} of child 2 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
selection end: position 3 of child 0 {#text} of child 2 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
<p>This tests for a bug where left justifying a paragraph amongst several that are centered would left justify all of them. Only the second paragraph should be left justified. </p>
<div id="div" contenteditable="true" style="text-align:center;">foo<br>bar<div>baz</div></div>
<script>
var div = document.getElementById("div");
var sel = window.getSelection();
sel.setPosition(div, 0);
sel.modify("move", "forward", "paragraph");
sel.modify("extend", "forward", "word");
document.execCommand("JustifyLeft");
</script>
\ No newline at end of file
......@@ -18,11 +18,10 @@ layer at (0,0) size 800x600
RenderBlock {DIV} at (14,14) size 756x28
RenderText {#text} at (362,0) size 32x28
text run at (362,0) width 32: "foo"
RenderBR {BR} at (394,22) size 0x0
RenderBlock (anonymous) at (14,42) size 756x56
RenderText {#text} at (0,0) size 31x28
text run at (0,0) width 31: "bar"
RenderBR {BR} at (31,22) size 0x0
RenderText {#text} at (0,28) size 34x28
text run at (0,28) width 34: "baz"
caret: position 1 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
caret: position 0 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
......@@ -23,7 +23,6 @@ layer at (0,0) size 800x600
RenderBlock {DIV} at (14,42) size 756x28
RenderText {#text} at (362,0) size 31x28
text run at (362,0) width 31: "bar"
RenderBR {BR} at (393,22) size 0x0
RenderBlock (anonymous) at (14,70) size 756x28
RenderText {#text} at (0,0) size 34x28
text run at (0,0) width 34: "baz"
......
......@@ -24,7 +24,6 @@ layer at (0,0) size 800x600
RenderBlock {DIV} at (14,14) size 756x28
RenderText {#text} at (362,0) size 32x28
text run at (362,0) width 32: "foo"
RenderBR {BR} at (394,22) size 0x0
RenderBlock (anonymous) at (14,42) size 756x28
RenderText {#text} at (0,0) size 31x28
text run at (0,0) width 31: "bar"
......
......@@ -25,4 +25,4 @@ layer at (0,0) size 800x600
RenderBlock (anonymous) at (14,98) size 756x28
RenderText {#text} at (0,0) size 34x28
text run at (0,0) width 34: "baz"
caret: position 1 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
caret: position 0 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
......@@ -27,4 +27,4 @@ layer at (0,0) size 800x600
RenderText {#text} at (361,0) size 34x28
text run at (361,0) width 34: "baz"
RenderBlock (anonymous) at (14,126) size 756x0
caret: position 1 of child 0 {#text} of child 2 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
caret: position 0 of child 0 {#text} of child 2 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
......@@ -3,7 +3,7 @@ EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotificatio
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 1 of #text > DIV > DIV > DIV > BODY > HTML > #document to 2 of #text > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of #text > DIV > DIV > DIV > BODY > HTML > #document to 2 of #text > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
......@@ -27,5 +27,5 @@ layer at (0,0) size 800x600
RenderBlock (anonymous) at (14,98) size 756x28
RenderText {#text} at (0,0) size 34x28
text run at (0,0) width 34: "baz"
selection start: position 1 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection start: position 0 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection end: position 2 of child 0 {#text} of child 1 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
......@@ -4,7 +4,7 @@ EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotificatio
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 1 of #text > DIV > DIV > DIV > BODY > HTML > #document to 2 of #text > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 1 of #text > DIV > DIV > DIV > BODY > HTML > #document to 1 of #text > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
......@@ -29,4 +29,4 @@ layer at (0,0) size 800x600
text run at (361,0) width 34: "baz"
RenderBlock (anonymous) at (14,126) size 756x0
selection start: position 1 of child 0 {#text} of child 1 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection end: position 2 of child 0 {#text} of child 2 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection end: position 1 of child 0 {#text} of child 2 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
......@@ -4,7 +4,7 @@ EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotificatio
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 1 of #text > DIV > DIV > DIV > DIV > BODY > HTML > #document to 1 of #text > DIV > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of #text > DIV > DIV > DIV > DIV > BODY > HTML > #document to 1 of #text > DIV > DIV > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
......@@ -25,10 +25,9 @@ layer at (0,0) size 800x600
RenderBlock {DIV} at (14,14) size 728x28
RenderText {#text} at (345,0) size 37x28
text run at (345,0) width 37: "bar "
RenderBR {BR} at (382,22) size 0x0
RenderBlock {DIV} at (14,42) size 728x28
RenderText {#text} at (347,0) size 34x28
text run at (347,0) width 34: "baz"
RenderBlock (anonymous) at (14,70) size 728x0
selection start: position 1 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection start: position 0 of child 0 {#text} of child 0 {DIV} of child 1 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
selection end: position 1 of child 0 {#text} of child 1 {DIV} of child 1 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
2007-03-13 Justin Garcia <justin.garcia@apple.com>
Reviewed by darin
<rdar://problem/5046875>
Gmail Editor: Applying alignment to selected text in message also applies alignment to signature
* editing/ApplyStyleCommand.cpp:
(WebCore::ApplyStyleCommand::doApply): Don't call applyBlockStyle unless
there is a block style to apply.
(WebCore::ApplyStyleCommand::applyBlockStyle): Don't do the remove step.
It was unnecessary and removed properties from blocks that could contain
content outside the range being operated on (added a testcase).
(WebCore::ApplyStyleCommand::addBlockStyleIfNeeded): Used an early return
instead of if-nesting.
* editing/ApplyStyleCommand.h:
* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary):
Return the new block, if one was created. Use moveParagraphs to move
paragraphs into the new block, instead of moving nodes. The old code moved
too much (added a testcase).
* editing/CompositeEditCommand.h:
2007-03-13 Oliver Hunt <oliver@apple.com>
 
Reviewed by Brady.
......
......@@ -27,9 +27,11 @@
#include "ApplyStyleCommand.h"
#include "CSSComputedStyleDeclaration.h"
#include "cssparser.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
#include "Document.h"
#include "htmlediting.h"
#include "HTMLElement.h"
#include "HTMLInterchange.h"
#include "HTMLNames.h"
......@@ -37,8 +39,8 @@
#include "Range.h"
#include "RenderObject.h"
#include "Text.h"
#include "cssparser.h"
#include "htmlediting.h"
#include "TextIterator.h"
#include "visible_units.h"
namespace WebCore {
......@@ -331,7 +333,8 @@ void ApplyStyleCommand::doApply()
case PropertyDefault: {
// apply the block-centric properties of the style
RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties();
applyBlockStyle(blockStyle.get());
if (blockStyle->length())
applyBlockStyle(blockStyle.get());
// apply any remaining styles to the inline elements
// NOTE: hopefully, this string comparison is the same as checking for a non-null diff
if (blockStyle->length() < m_style->length() || m_styledInlineElement) {
......@@ -369,49 +372,28 @@ void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style)
start = end;
end = swap;
}
// remove current values, if any, of the specified styles from the blocks
// NOTE: tracks the previous block to avoid repeated processing
// Also, gather up all the nodes we want to process in a Vector before
// doing anything. This averts any bugs iterating over these nodes
// once you start removing and applying style.
// If the end node is before the start node (can only happen if the end node is
// an ancestor of the start node), we gather nodes up to the next sibling of the end node
Node *beyondEnd;
if (start.node()->isDescendantOf(end.node()))
beyondEnd = end.node()->traverseNextSibling();
else
beyondEnd = end.node()->traverseNextNode();
Vector<Node*> nodes;
for (Node *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
nodes.append(node);
Node *prevBlock = 0;
for (unsigned i = 0; i < nodes.size(); i++) {
Node *block = nodes[i]->enclosingBlockFlowElement();
if (block != prevBlock && block->isHTMLElement()) {
removeCSSStyle(style, static_cast<HTMLElement *>(block));
prevBlock = block;
}
}
if (m_removeOnly)
return;
VisiblePosition visibleStart(start);
VisiblePosition visibleEnd(end);
// Save and restore the selection endpoints using their indices in the document, since
// addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
RefPtr<Range> startRange = new Range(document(), Position(document(), 0), visibleStart.deepEquivalent());
RefPtr<Range> endRange = new Range(document(), Position(document(), 0), visibleEnd.deepEquivalent());
int startIndex = TextIterator::rangeLength(startRange.get());
int endIndex = TextIterator::rangeLength(endRange.get());
// apply specified styles to the block flow elements in the selected range
prevBlock = 0;
for (unsigned i = 0; i < nodes.size(); i++) {
Node *node = nodes[i];
if (node->renderer()) {
Node *block = node->enclosingBlockFlowElement();
if (block != prevBlock) {
addBlockStyleIfNeeded(style, node);
prevBlock = block;
}
}
VisiblePosition paragraphStart(startOfParagraph(visibleStart));
VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
addBlockStyleIfNeeded(style, paragraphStart);
paragraphStart = nextParagraphStart;
nextParagraphStart = endOfParagraph(paragraphStart).next();
}
setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0)->startPosition(),
TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0)->startPosition(),
DOWNSTREAM));
}
#define NoFontDelta (0.0f)
......@@ -1216,27 +1198,37 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(Node *startNode, Node *endN
}
}
void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *node)
void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclaration* style, const VisiblePosition& paragraphStart)
{
// Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
// inline content.
if (!node)
if (paragraphStart.isNull())
return;
HTMLElement *block = static_cast<HTMLElement *>(node->enclosingBlockFlowElement());
Node* block = enclosingBlock(paragraphStart.deepEquivalent().node());
if (!block)
return;
StyleChange styleChange(style, Position(block, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
if (styleChange.cssStyle().length() > 0) {
moveParagraphContentsToNewBlockIfNecessary(Position(node, 0));
block = static_cast<HTMLElement *>(node->enclosingBlockFlowElement());
String cssText = styleChange.cssStyle();
CSSMutableStyleDeclaration *decl = block->inlineStyleDecl();
if (decl)
cssText += decl->cssText();
setNodeAttribute(block, styleAttr, cssText);
}
if (styleChange.cssStyle().length() == 0)
return;
// If a new block was necessary, this function uses moveParagraphs to move content into
// the new block, which invalidates block.
Node* newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
if (newBlock)
block = newBlock;
ASSERT(block->isHTMLElement());
if (!block->isHTMLElement())
return;
String cssText = styleChange.cssStyle();
CSSMutableStyleDeclaration* decl = static_cast<HTMLElement*>(block)->inlineStyleDecl();
if (decl)
cssText += decl->cssText();
setNodeAttribute(static_cast<Element*>(block), styleAttr, cssText);
}
void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode)
......
......@@ -65,7 +65,7 @@ private:
void applyBlockStyle(CSSMutableStyleDeclaration*);
void applyRelativeFontStyleChange(CSSMutableStyleDeclaration*);
void applyInlineStyle(CSSMutableStyleDeclaration*);
void addBlockStyleIfNeeded(CSSMutableStyleDeclaration*, Node*);
void addBlockStyleIfNeeded(CSSMutableStyleDeclaration*, const VisiblePosition&);
void addInlineStyleIfNeeded(CSSMutableStyleDeclaration*, Node* start, Node* end);
bool splitTextAtStartIfNeeded(const Position& start, const Position& end);
bool splitTextAtEndIfNeeded(const Position& start, const Position& end);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment