72 static gchar* textForRenderer(RenderObject* renderer)
73 {
74 GString* resultText = g_string_new(0);
75
76 if (!renderer)
77 return g_string_free(resultText, FALSE);
78
79 // For RenderBlocks, piece together the text from the RenderText objects they contain.
80 for (RenderObject* object = renderer->firstChildSlow(); object; object = object->nextSibling()) {
81 if (object->isBR()) {
82 g_string_append(resultText, "\n");
83 continue;
84 }
85
86 RenderText* renderText;
87 if (object->isText())
88 renderText = toRenderText(object);
89 else {
90 // List item's markers will be treated in an special way
91 // later on this function, so ignore them here.
92 if (object->isReplaced() && !object->isListMarker())
93 g_string_append_unichar(resultText, objectReplacementCharacter);
94
95 // We need to check children, if any, to consider when
96 // current object is not a text object but some of its
97 // children are, in order not to miss those portions of
98 // text by not properly handling those situations
99 if (object->firstChildSlow()) {
100 GOwnPtr<char> objectText(textForRenderer(object));
101 g_string_append(resultText, objectText.get());
102 }
103 continue;
104 }
105
106 InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
107 while (box) {
108 // WebCore introduces line breaks in the text that do not reflect
109 // the layout you see on the screen, replace them with spaces.
110 String text = String(renderText->characters(), renderText->textLength()).replace("\n", " ");
111 g_string_append(resultText, text.substring(box->start(), box->end() - box->start() + 1).utf8().data());
112
113 // Newline chars in the source result in separate text boxes, so check
114 // before adding a newline in the layout. See bug 25415 comment #78.
115 // If the next sibling is a BR, we'll add the newline when we examine that child.
116 if (!box->nextOnLineExists() && !(object->nextSibling() && object->nextSibling()->isBR())) {
117 // If there was a '\n' in the last position of the
118 // current text box, it would have been converted to a
119 // space in String::replace(), so remove it first.
120 if (renderText->characters()[box->end()] == '\n')
121 g_string_erase(resultText, resultText->len - 1, -1);
122
123 g_string_append(resultText, "\n");
124 }
125 box = box->nextTextBox();
126 }
127 }
128
129 // Insert the text of the marker for list item in the right place, if present
130 if (renderer->isListItem()) {
131 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
132 if (renderer->style()->direction() == LTR)
133 g_string_prepend(resultText, markerText.utf8().data());
134 else
135 g_string_append(resultText, markerText.utf8().data());
136 }
137
138 return g_string_free(resultText, FALSE);
139 }
140
141 static gchar* textForObject(const AccessibilityObject* coreObject)
142 {
143 GString* str = g_string_new(0);
144
145 // For text controls, we can get the text line by line.
146 if (coreObject->isTextControl()) {
147 unsigned textLength = coreObject->textLength();
148 int lineNumber = 0;
149 PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
150 while (range.length) {
151 String lineText = coreObject->doAXStringForRange(range);
152 g_string_append(str, lineText.utf8().data());
153
154 // When a line of text wraps in a text area, the final space
155 // after each non-final line must be replaced with a line break.
156 if (range.start + range.length < textLength)
157 g_string_overwrite_len(str, str->len - 1, "\n", 1);
158
159 range = coreObject->doAXRangeForLine(++lineNumber);
160 }
161 } else if (coreObject->isAccessibilityRenderObject()) {
162 GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
163 g_string_append(str, rendererText.get());
164 }
165
166 return g_string_free(str, FALSE);
167 }
168