|
1 /** |
|
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
|
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
|
4 * (C) 2000 Dirk Mueller (mueller@kde.org) |
|
5 * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) |
|
6 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
|
7 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) |
|
8 * |
|
9 * This library is free software; you can redistribute it and/or |
|
10 * modify it under the terms of the GNU Library General Public |
|
11 * License as published by the Free Software Foundation; either |
|
12 * version 2 of the License, or (at your option) any later version. |
|
13 * |
|
14 * This library is distributed in the hope that it will be useful, |
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 * Library General Public License for more details. |
|
18 * |
|
19 * You should have received a copy of the GNU Library General Public License |
|
20 * along with this library; see the file COPYING.LIB. If not, write to |
|
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
22 * Boston, MA 02110-1301, USA. |
|
23 * |
|
24 */ |
|
25 |
|
26 #include "config.h" |
|
27 #include "RenderContainer.h" |
|
28 |
|
29 #include "AXObjectCache.h" |
|
30 #include "Document.h" |
|
31 #include "RenderCounter.h" |
|
32 #include "RenderImage.h" |
|
33 #include "RenderLayer.h" |
|
34 #include "RenderListItem.h" |
|
35 #include "RenderTable.h" |
|
36 #include "RenderTextFragment.h" |
|
37 #include "RenderView.h" |
|
38 #include "htmlediting.h" |
|
39 |
|
40 namespace WebCore { |
|
41 |
|
42 RenderContainer::RenderContainer(Node* node) |
|
43 : RenderBox(node) |
|
44 , m_firstChild(0) |
|
45 , m_lastChild(0) |
|
46 { |
|
47 } |
|
48 |
|
49 RenderContainer::~RenderContainer() |
|
50 { |
|
51 } |
|
52 |
|
53 void RenderContainer::destroy() |
|
54 { |
|
55 destroyLeftoverChildren(); |
|
56 RenderBox::destroy(); |
|
57 } |
|
58 |
|
59 void RenderContainer::destroyLeftoverChildren() |
|
60 { |
|
61 while (m_firstChild) { |
|
62 if (m_firstChild->isListMarker() || (m_firstChild->style()->styleType() == RenderStyle::FIRST_LETTER && !m_firstChild->isText())) |
|
63 m_firstChild->remove(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment. |
|
64 else { |
|
65 // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. |
|
66 if (m_firstChild->element()) |
|
67 m_firstChild->element()->setRenderer(0); |
|
68 m_firstChild->destroy(); |
|
69 } |
|
70 } |
|
71 } |
|
72 |
|
73 bool RenderContainer::canHaveChildren() const |
|
74 { |
|
75 return true; |
|
76 } |
|
77 |
|
78 static void updateListMarkerNumbers(RenderObject* child) |
|
79 { |
|
80 for (RenderObject* r = child; r; r = r->nextSibling()) |
|
81 if (r->isListItem()) |
|
82 static_cast<RenderListItem*>(r)->updateValue(); |
|
83 } |
|
84 |
|
85 void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) |
|
86 { |
|
87 bool needsTable = false; |
|
88 |
|
89 if (newChild->isListItem()) |
|
90 updateListMarkerNumbers(beforeChild ? beforeChild : m_lastChild); |
|
91 else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) |
|
92 needsTable = !isTable(); |
|
93 else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) |
|
94 needsTable = !isTable(); |
|
95 else if (newChild->isTableSection()) |
|
96 needsTable = !isTable(); |
|
97 else if (newChild->isTableRow()) |
|
98 needsTable = !isTableSection(); |
|
99 else if (newChild->isTableCell()) { |
|
100 needsTable = !isTableRow(); |
|
101 // I'm not 100% sure this is the best way to fix this, but without this |
|
102 // change we recurse infinitely when trying to render the CSS2 test page: |
|
103 // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. |
|
104 // See Radar 2925291. |
|
105 if (needsTable && isTableCell() && !m_firstChild && !newChild->isTableCell()) |
|
106 needsTable = false; |
|
107 } |
|
108 |
|
109 if (needsTable) { |
|
110 RenderTable *table; |
|
111 if(!beforeChild) |
|
112 beforeChild = m_lastChild; |
|
113 if(beforeChild && beforeChild->isAnonymous() && beforeChild->isTable()) |
|
114 table = static_cast<RenderTable*>(beforeChild); |
|
115 else { |
|
116 table = new (renderArena()) RenderTable(document() /* is anonymous */); |
|
117 RenderStyle *newStyle = new (renderArena()) RenderStyle; |
|
118 newStyle->inheritFrom(style()); |
|
119 newStyle->setDisplay(TABLE); |
|
120 table->setStyle(newStyle); |
|
121 addChild(table, beforeChild); |
|
122 } |
|
123 table->addChild(newChild); |
|
124 } else { |
|
125 // just add it... |
|
126 insertChildNode(newChild, beforeChild); |
|
127 } |
|
128 |
|
129 if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { |
|
130 RefPtr<StringImpl> textToTransform = static_cast<RenderText*>(newChild)->originalText(); |
|
131 if (textToTransform) |
|
132 static_cast<RenderText*>(newChild)->setText(textToTransform.release(), true); |
|
133 } |
|
134 } |
|
135 |
|
136 RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool fullRemove) |
|
137 { |
|
138 ASSERT(oldChild->parent() == this); |
|
139 |
|
140 // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or |
|
141 // that a positioned child got yanked). We also repaint, so that the area exposed when the child |
|
142 // disappears gets repainted properly. |
|
143 if (!documentBeingDestroyed() && fullRemove) { |
|
144 oldChild->setNeedsLayoutAndPrefWidthsRecalc(); |
|
145 oldChild->repaint(); |
|
146 } |
|
147 |
|
148 // If we have a line box wrapper, delete it. |
|
149 oldChild->deleteLineBoxWrapper(); |
|
150 |
|
151 if (!documentBeingDestroyed() && fullRemove) { |
|
152 // if we remove visible child from an invisible parent, we don't know the layer visibility any more |
|
153 RenderLayer* layer = 0; |
|
154 if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) { |
|
155 layer = enclosingLayer(); |
|
156 layer->dirtyVisibleContentStatus(); |
|
157 } |
|
158 |
|
159 // Keep our layer hierarchy updated. |
|
160 if (oldChild->firstChild() || oldChild->hasLayer()) { |
|
161 if (!layer) layer = enclosingLayer(); |
|
162 oldChild->removeLayers(layer); |
|
163 } |
|
164 |
|
165 // renumber ordered lists |
|
166 if (oldChild->isListItem()) |
|
167 updateListMarkerNumbers(oldChild->nextSibling()); |
|
168 |
|
169 if (oldChild->isPositioned() && childrenInline()) |
|
170 dirtyLinesFromChangedChild(oldChild); |
|
171 } |
|
172 |
|
173 // If oldChild is the start or end of the selection, then clear the selection to |
|
174 // avoid problems of invalid pointers. |
|
175 // FIXME: The SelectionController should be responsible for this when it |
|
176 // is notified of DOM mutations. |
|
177 if (!documentBeingDestroyed() && oldChild->isSelectionBorder()) |
|
178 view()->clearSelection(); |
|
179 |
|
180 // remove the child |
|
181 if (oldChild->previousSibling()) |
|
182 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); |
|
183 if (oldChild->nextSibling()) |
|
184 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); |
|
185 |
|
186 if (m_firstChild == oldChild) |
|
187 m_firstChild = oldChild->nextSibling(); |
|
188 if (m_lastChild == oldChild) |
|
189 m_lastChild = oldChild->previousSibling(); |
|
190 |
|
191 oldChild->setPreviousSibling(0); |
|
192 oldChild->setNextSibling(0); |
|
193 oldChild->setParent(0); |
|
194 |
|
195 if (AXObjectCache::accessibilityEnabled()) |
|
196 document()->axObjectCache()->childrenChanged(this); |
|
197 |
|
198 return oldChild; |
|
199 } |
|
200 |
|
201 void RenderContainer::removeChild(RenderObject* oldChild) |
|
202 { |
|
203 // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode |
|
204 // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on |
|
205 // layout anyway). |
|
206 oldChild->removeFromObjectLists(); |
|
207 |
|
208 removeChildNode(oldChild); |
|
209 } |
|
210 |
|
211 RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type) |
|
212 { |
|
213 if (type == RenderStyle::BEFORE) { |
|
214 RenderObject* first = this; |
|
215 do { |
|
216 // Skip list markers. |
|
217 first = first->firstChild(); |
|
218 while (first && first->isListMarker()) |
|
219 first = first->nextSibling(); |
|
220 } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO); |
|
221 if (first && first->style()->styleType() != type) |
|
222 return 0; |
|
223 return first; |
|
224 } |
|
225 if (type == RenderStyle::AFTER) { |
|
226 RenderObject* last = this; |
|
227 do { |
|
228 last = last->lastChild(); |
|
229 } while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker()); |
|
230 if (last && last->style()->styleType() != type) |
|
231 return 0; |
|
232 return last; |
|
233 } |
|
234 |
|
235 ASSERT_NOT_REACHED(); |
|
236 return 0; |
|
237 } |
|
238 |
|
239 void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type) |
|
240 { |
|
241 // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. |
|
242 if (parent() && parent()->createsAnonymousWrapper()) |
|
243 return; |
|
244 updateBeforeAfterContentForContainer(type, this); |
|
245 } |
|
246 |
|
247 void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject) |
|
248 { |
|
249 // In CSS2, before/after pseudo-content cannot nest. Check this first. |
|
250 if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER) |
|
251 return; |
|
252 |
|
253 RenderStyle* pseudoElementStyle = styledObject->getPseudoStyle(type); |
|
254 RenderObject* child = beforeAfterContainer(type); |
|
255 |
|
256 // Whether or not we currently have generated content attached. |
|
257 bool oldContentPresent = child; |
|
258 |
|
259 // Whether or not we now want generated content. |
|
260 bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; |
|
261 |
|
262 // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate |
|
263 // :after content and not :before content. |
|
264 if (newContentWanted && type == RenderStyle::BEFORE && isInlineContinuation()) |
|
265 newContentWanted = false; |
|
266 |
|
267 // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, |
|
268 // then we don't generate the :after content. |
|
269 if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && continuation()) |
|
270 newContentWanted = false; |
|
271 |
|
272 // If we don't want generated content any longer, or if we have generated content, but it's no longer |
|
273 // identical to the new content data we want to build render objects for, then we nuke all |
|
274 // of the old generated content. |
|
275 if (!newContentWanted || (oldContentPresent && !child->style()->contentDataEquivalent(pseudoElementStyle))) { |
|
276 // Nuke the child. |
|
277 if (child && child->style()->styleType() == type) { |
|
278 oldContentPresent = false; |
|
279 child->destroy(); |
|
280 child = (type == RenderStyle::BEFORE) ? m_firstChild : m_lastChild; |
|
281 } |
|
282 } |
|
283 |
|
284 // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we |
|
285 // have no generated content and can now return. |
|
286 if (!newContentWanted) |
|
287 return; |
|
288 |
|
289 if (isInlineFlow() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE && |
|
290 !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition)) |
|
291 // According to the CSS2 spec (the end of section 12.1), the only allowed |
|
292 // display values for the pseudo style are NONE and INLINE for inline flows. |
|
293 // FIXME: CSS2.1 lifted this restriction, but block display types will crash. |
|
294 // For now we at least relax the restriction to allow all inline types like inline-block |
|
295 // and inline-table. |
|
296 pseudoElementStyle->setDisplay(INLINE); |
|
297 |
|
298 if (oldContentPresent) { |
|
299 if (child && child->style()->styleType() == type) { |
|
300 // We have generated content present still. We want to walk this content and update our |
|
301 // style information with the new pseudo-element style. |
|
302 child->setStyle(pseudoElementStyle); |
|
303 |
|
304 // Note that if we ever support additional types of generated content (which should be way off |
|
305 // in the future), this code will need to be patched. |
|
306 for (RenderObject* genChild = child->firstChild(); genChild; genChild = genChild->nextSibling()) { |
|
307 if (genChild->isText()) |
|
308 // Generated text content is a child whose style also needs to be set to the pseudo-element style. |
|
309 genChild->setStyle(pseudoElementStyle); |
|
310 else if (genChild->isImage()) { |
|
311 // Images get an empty style that inherits from the pseudo. |
|
312 RenderStyle* style = new (renderArena()) RenderStyle; |
|
313 style->inheritFrom(pseudoElementStyle); |
|
314 genChild->setStyle(style); |
|
315 } else |
|
316 // Must be a first-letter container. updateFirstLetter() will take care of it. |
|
317 ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER); |
|
318 } |
|
319 } |
|
320 return; // We've updated the generated content. That's all we needed to do. |
|
321 } |
|
322 |
|
323 RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? firstChild() : 0; |
|
324 |
|
325 // Generated content consists of a single container that houses multiple children (specified |
|
326 // by the content property). This generated content container gets the pseudo-element style set on it. |
|
327 RenderObject* generatedContentContainer = 0; |
|
328 |
|
329 // Walk our list of generated content and create render objects for each. |
|
330 for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) { |
|
331 RenderObject* renderer = 0; |
|
332 switch (content->m_type) { |
|
333 case CONTENT_NONE: |
|
334 break; |
|
335 case CONTENT_TEXT: |
|
336 renderer = new (renderArena()) RenderTextFragment(document() /* anonymous object */, content->m_content.m_text); |
|
337 renderer->setStyle(pseudoElementStyle); |
|
338 break; |
|
339 case CONTENT_OBJECT: |
|
340 if (CachedResource* resource = content->m_content.m_object) |
|
341 if (resource->type() == CachedResource::ImageResource) { |
|
342 RenderImage* image = new (renderArena()) RenderImage(document()); // anonymous object |
|
343 RenderStyle* style = new (renderArena()) RenderStyle; |
|
344 style->inheritFrom(pseudoElementStyle); |
|
345 image->setStyle(style); |
|
346 image->setCachedImage(static_cast<CachedImage*>(resource)); |
|
347 image->setIsAnonymousImage(true); |
|
348 renderer = image; |
|
349 } |
|
350 break; |
|
351 case CONTENT_COUNTER: |
|
352 renderer = new (renderArena()) RenderCounter(document(), *content->m_content.m_counter); |
|
353 renderer->setStyle(pseudoElementStyle); |
|
354 break; |
|
355 } |
|
356 |
|
357 if (renderer) { |
|
358 if (!generatedContentContainer) { |
|
359 // Make a generated box that might be any display type now that we are able to drill down into children |
|
360 // to find the original content properly. |
|
361 generatedContentContainer = RenderObject::createObject(document(), pseudoElementStyle); |
|
362 generatedContentContainer->setStyle(pseudoElementStyle); |
|
363 } |
|
364 generatedContentContainer->addChild(renderer); |
|
365 } |
|
366 } |
|
367 |
|
368 // Add the pseudo after we've installed all our content so that addChild will be able to find the text |
|
369 // inside the inline for e.g., first-letter styling. |
|
370 if (generatedContentContainer) |
|
371 addChild(generatedContentContainer, insertBefore); |
|
372 } |
|
373 |
|
374 bool RenderContainer::isAfterContent(RenderObject* child) const |
|
375 { |
|
376 if (!child) |
|
377 return false; |
|
378 if (child->style()->styleType() != RenderStyle::AFTER) |
|
379 return false; |
|
380 // Text nodes don't have their own styles, so ignore the style on a text node. |
|
381 if (child->isText() && !child->isBR()) |
|
382 return false; |
|
383 return true; |
|
384 } |
|
385 |
|
386 void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend) |
|
387 { |
|
388 ASSERT(newChild->parent() == 0); |
|
389 ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); |
|
390 |
|
391 newChild->setParent(this); |
|
392 RenderObject* lChild = m_lastChild; |
|
393 |
|
394 if (lChild) { |
|
395 newChild->setPreviousSibling(lChild); |
|
396 lChild->setNextSibling(newChild); |
|
397 } else |
|
398 m_firstChild = newChild; |
|
399 |
|
400 m_lastChild = newChild; |
|
401 |
|
402 if (fullAppend) { |
|
403 // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children |
|
404 // and don't have a layer attached to ourselves. |
|
405 RenderLayer* layer = 0; |
|
406 if (newChild->firstChild() || newChild->hasLayer()) { |
|
407 layer = enclosingLayer(); |
|
408 newChild->addLayers(layer, newChild); |
|
409 } |
|
410 |
|
411 // if the new child is visible but this object was not, tell the layer it has some visible content |
|
412 // that needs to be drawn and layer visibility optimization can't be used |
|
413 if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) { |
|
414 if (!layer) |
|
415 layer = enclosingLayer(); |
|
416 if (layer) |
|
417 layer->setHasVisibleContent(true); |
|
418 } |
|
419 |
|
420 if (!newChild->isFloatingOrPositioned() && childrenInline()) |
|
421 dirtyLinesFromChangedChild(newChild); |
|
422 } |
|
423 |
|
424 newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. |
|
425 if (!normalChildNeedsLayout()) |
|
426 setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. |
|
427 |
|
428 if (AXObjectCache::accessibilityEnabled()) |
|
429 document()->axObjectCache()->childrenChanged(this); |
|
430 } |
|
431 |
|
432 void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert) |
|
433 { |
|
434 if (!beforeChild) { |
|
435 appendChildNode(child); |
|
436 return; |
|
437 } |
|
438 |
|
439 ASSERT(!child->parent()); |
|
440 while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock()) |
|
441 beforeChild = beforeChild->parent(); |
|
442 ASSERT(beforeChild->parent() == this); |
|
443 |
|
444 ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); |
|
445 |
|
446 if (beforeChild == m_firstChild) |
|
447 m_firstChild = child; |
|
448 |
|
449 RenderObject* prev = beforeChild->previousSibling(); |
|
450 child->setNextSibling(beforeChild); |
|
451 beforeChild->setPreviousSibling(child); |
|
452 if(prev) prev->setNextSibling(child); |
|
453 child->setPreviousSibling(prev); |
|
454 |
|
455 child->setParent(this); |
|
456 |
|
457 if (fullInsert) { |
|
458 // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children |
|
459 // and don't have a layer attached to ourselves. |
|
460 RenderLayer* layer = 0; |
|
461 if (child->firstChild() || child->hasLayer()) { |
|
462 layer = enclosingLayer(); |
|
463 child->addLayers(layer, child); |
|
464 } |
|
465 |
|
466 // if the new child is visible but this object was not, tell the layer it has some visible content |
|
467 // that needs to be drawn and layer visibility optimization can't be used |
|
468 if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) { |
|
469 if (!layer) |
|
470 layer = enclosingLayer(); |
|
471 if (layer) |
|
472 layer->setHasVisibleContent(true); |
|
473 } |
|
474 |
|
475 |
|
476 if (!child->isFloating() && childrenInline()) |
|
477 dirtyLinesFromChangedChild(child); |
|
478 } |
|
479 |
|
480 child->setNeedsLayoutAndPrefWidthsRecalc(); |
|
481 if (!normalChildNeedsLayout()) |
|
482 setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. |
|
483 |
|
484 if (AXObjectCache::accessibilityEnabled()) |
|
485 document()->axObjectCache()->childrenChanged(this); |
|
486 } |
|
487 |
|
488 void RenderContainer::layout() |
|
489 { |
|
490 ASSERT(needsLayout()); |
|
491 |
|
492 view()->pushLayoutState(this, IntSize(m_x, m_y)); |
|
493 |
|
494 RenderObject* child = m_firstChild; |
|
495 while (child) { |
|
496 child->layoutIfNeeded(); |
|
497 ASSERT(child->isRenderInline() || !child->needsLayout()); |
|
498 child = child->nextSibling(); |
|
499 } |
|
500 |
|
501 view()->popLayoutState(); |
|
502 setNeedsLayout(false); |
|
503 } |
|
504 |
|
505 void RenderContainer::removeLeftoverAnonymousBlock(RenderBlock* child) |
|
506 { |
|
507 ASSERT(child->isAnonymousBlock()); |
|
508 ASSERT(!child->childrenInline()); |
|
509 |
|
510 if (child->continuation()) |
|
511 return; |
|
512 |
|
513 RenderObject* firstAnChild = child->firstChild(); |
|
514 RenderObject* lastAnChild = child->lastChild(); |
|
515 if (firstAnChild) { |
|
516 RenderObject* o = firstAnChild; |
|
517 while(o) { |
|
518 o->setParent(this); |
|
519 o = o->nextSibling(); |
|
520 } |
|
521 firstAnChild->setPreviousSibling(child->previousSibling()); |
|
522 lastAnChild->setNextSibling(child->nextSibling()); |
|
523 if (child->previousSibling()) |
|
524 child->previousSibling()->setNextSibling(firstAnChild); |
|
525 if (child->nextSibling()) |
|
526 child->nextSibling()->setPreviousSibling(lastAnChild); |
|
527 } else { |
|
528 if (child->previousSibling()) |
|
529 child->previousSibling()->setNextSibling(child->nextSibling()); |
|
530 if (child->nextSibling()) |
|
531 child->nextSibling()->setPreviousSibling(child->previousSibling()); |
|
532 } |
|
533 if (child == m_firstChild) |
|
534 m_firstChild = firstAnChild; |
|
535 if (child == m_lastChild) |
|
536 m_lastChild = lastAnChild; |
|
537 child->setParent(0); |
|
538 child->setPreviousSibling(0); |
|
539 child->setNextSibling(0); |
|
540 if (!child->isText()) { |
|
541 RenderContainer *c = static_cast<RenderContainer*>(child); |
|
542 c->m_firstChild = 0; |
|
543 c->m_next = 0; |
|
544 } |
|
545 child->destroy(); |
|
546 } |
|
547 |
|
548 VisiblePosition RenderContainer::positionForCoordinates(int x, int y) |
|
549 { |
|
550 // no children...return this render object's element, if there is one, and offset 0 |
|
551 if (!m_firstChild) |
|
552 return VisiblePosition(element(), 0, DOWNSTREAM); |
|
553 |
|
554 if (isTable() && element()) { |
|
555 int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft(); |
|
556 int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom(); |
|
557 |
|
558 if (x < 0 || x > right || y < 0 || y > bottom) { |
|
559 if (x <= right / 2) |
|
560 return VisiblePosition(Position(element(), 0)); |
|
561 else |
|
562 return VisiblePosition(Position(element(), maxDeepOffset(element()))); |
|
563 } |
|
564 } |
|
565 |
|
566 // Pass off to the closest child. |
|
567 int minDist = INT_MAX; |
|
568 RenderObject* closestRenderer = 0; |
|
569 int newX = x; |
|
570 int newY = y; |
|
571 if (isTableRow()) { |
|
572 newX += xPos(); |
|
573 newY += yPos(); |
|
574 } |
|
575 for (RenderObject* renderer = m_firstChild; renderer; renderer = renderer->nextSibling()) { |
|
576 if (!renderer->firstChild() && !renderer->isInline() && !renderer->isBlockFlow() |
|
577 || renderer->style()->visibility() != VISIBLE) |
|
578 continue; |
|
579 |
|
580 int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->yPos()); |
|
581 int bottom = top + renderer->contentHeight(); |
|
582 int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer->xPos()); |
|
583 int right = left + renderer->contentWidth(); |
|
584 |
|
585 if (x <= right && x >= left && y <= top && y >= bottom) { |
|
586 if (renderer->isTableRow()) |
|
587 return renderer->positionForCoordinates(x + newX - renderer->xPos(), y + newY - renderer->yPos()); |
|
588 return renderer->positionForCoordinates(x - renderer->xPos(), y - renderer->yPos()); |
|
589 } |
|
590 |
|
591 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces |
|
592 // and use a different compare depending on which piece (x, y) is in. |
|
593 IntPoint cmp; |
|
594 if (x > right) { |
|
595 if (y < top) |
|
596 cmp = IntPoint(right, top); |
|
597 else if (y > bottom) |
|
598 cmp = IntPoint(right, bottom); |
|
599 else |
|
600 cmp = IntPoint(right, y); |
|
601 } else if (x < left) { |
|
602 if (y < top) |
|
603 cmp = IntPoint(left, top); |
|
604 else if (y > bottom) |
|
605 cmp = IntPoint(left, bottom); |
|
606 else |
|
607 cmp = IntPoint(left, y); |
|
608 } else { |
|
609 if (y < top) |
|
610 cmp = IntPoint(x, top); |
|
611 else |
|
612 cmp = IntPoint(x, bottom); |
|
613 } |
|
614 |
|
615 int x1minusx2 = cmp.x() - x; |
|
616 int y1minusy2 = cmp.y() - y; |
|
617 |
|
618 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; |
|
619 if (dist < minDist) { |
|
620 closestRenderer = renderer; |
|
621 minDist = dist; |
|
622 } |
|
623 } |
|
624 |
|
625 if (closestRenderer) |
|
626 return closestRenderer->positionForCoordinates(newX - closestRenderer->xPos(), newY - closestRenderer->yPos()); |
|
627 |
|
628 return VisiblePosition(element(), 0, DOWNSTREAM); |
|
629 } |
|
630 |
|
631 void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end) |
|
632 { |
|
633 if (!m_firstChild && (isInline() || isAnonymousBlock())) { |
|
634 int x, y; |
|
635 absolutePositionForContent(x, y); |
|
636 absoluteRects(rects, x, y); |
|
637 return; |
|
638 } |
|
639 |
|
640 if (!m_firstChild) |
|
641 return; |
|
642 |
|
643 unsigned offset = start; |
|
644 for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { |
|
645 if (child->isText() || child->isInline() || child->isAnonymousBlock()) { |
|
646 int x, y; |
|
647 child->absolutePositionForContent(x, y); |
|
648 child->absoluteRects(rects, x, y); |
|
649 } |
|
650 } |
|
651 } |
|
652 |
|
653 #undef DEBUG_LAYOUT |
|
654 |
|
655 } // namespace WebCore |