Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: third_party/WebKit/WebCore/rendering/RenderContainer.cpp

Issue 20076: WebKit merge 40500:40539 [WebKit side] (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /** 1 /**
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2000 Dirk Mueller (mueller@kde.org) 4 * (C) 2000 Dirk Mueller (mueller@kde.org)
5 * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) 5 * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
6 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. 6 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
7 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) 7 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
8 * 8 *
9 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public 10 * modify it under the terms of the GNU Library General Public
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 table = new (renderArena()) RenderTable(document() /* is anonymous * /); 89 table = new (renderArena()) RenderTable(document() /* is anonymous * /);
90 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 90 RefPtr<RenderStyle> newStyle = RenderStyle::create();
91 newStyle->inheritFrom(style()); 91 newStyle->inheritFrom(style());
92 newStyle->setDisplay(TABLE); 92 newStyle->setDisplay(TABLE);
93 table->setStyle(newStyle.release()); 93 table->setStyle(newStyle.release());
94 addChild(table, beforeChild); 94 addChild(table, beforeChild);
95 } 95 }
96 table->addChild(newChild); 96 table->addChild(newChild);
97 } else { 97 } else {
98 // just add it... 98 // just add it...
99 insertChildNode(newChild, beforeChild); 99 children()->insertChildNode(this, newChild, beforeChild);
100 } 100 }
101 101
102 if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { 102 if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
103 RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalTex t(); 103 RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalTex t();
104 if (textToTransform) 104 if (textToTransform)
105 toRenderText(newChild)->setText(textToTransform.release(), true); 105 toRenderText(newChild)->setText(textToTransform.release(), true);
106 } 106 }
107 } 107 }
108 108
109 RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool full Remove)
110 {
111 ASSERT(oldChild->parent() == this);
112
113 // So that we'll get the appropriate dirty bit set (either that a normal flo w child got yanked or
114 // that a positioned child got yanked). We also repaint, so that the area e xposed when the child
115 // disappears gets repainted properly.
116 if (!documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) {
117 oldChild->setNeedsLayoutAndPrefWidthsRecalc();
118 oldChild->repaint();
119 }
120
121 // If we have a line box wrapper, delete it.
122 oldChild->deleteLineBoxWrapper();
123
124 if (!documentBeingDestroyed() && fullRemove) {
125 // if we remove visible child from an invisible parent, we don't know th e layer visibility any more
126 RenderLayer* layer = 0;
127 if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) {
128 layer = enclosingLayer();
129 layer->dirtyVisibleContentStatus();
130 }
131
132 // Keep our layer hierarchy updated.
133 if (oldChild->firstChild() || oldChild->hasLayer()) {
134 if (!layer) layer = enclosingLayer();
135 oldChild->removeLayers(layer);
136 }
137
138 // renumber ordered lists
139 if (oldChild->isListItem())
140 updateListMarkerNumbers(oldChild->nextSibling());
141
142 if (oldChild->isPositioned() && childrenInline())
143 dirtyLinesFromChangedChild(oldChild);
144 }
145
146 // If oldChild is the start or end of the selection, then clear the selectio n to
147 // avoid problems of invalid pointers.
148 // FIXME: The SelectionController should be responsible for this when it
149 // is notified of DOM mutations.
150 if (!documentBeingDestroyed() && oldChild->isSelectionBorder())
151 view()->clearSelection();
152
153 // remove the child
154 if (oldChild->previousSibling())
155 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
156 if (oldChild->nextSibling())
157 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()) ;
158
159 if (children()->firstChild() == oldChild)
160 children()->setFirstChild(oldChild->nextSibling());
161 if (children()->lastChild() == oldChild)
162 children()->setLastChild(oldChild->previousSibling());
163
164 oldChild->setPreviousSibling(0);
165 oldChild->setNextSibling(0);
166 oldChild->setParent(0);
167
168 if (AXObjectCache::accessibilityEnabled())
169 document()->axObjectCache()->childrenChanged(this);
170
171 return oldChild;
172 }
173
174 void RenderContainer::removeChild(RenderObject* oldChild) 109 void RenderContainer::removeChild(RenderObject* oldChild)
175 { 110 {
176 // We do this here instead of in removeChildNode, since the only extremely l ow-level uses of remove/appendChildNode 111 // We do this here instead of in removeChildNode, since the only extremely l ow-level uses of remove/appendChildNode
177 // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on 112 // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
178 // layout anyway). 113 // layout anyway).
179 oldChild->removeFromObjectLists(); 114 oldChild->removeFromObjectLists();
180 115 m_children.removeChildNode(this, oldChild);
181 removeChildNode(oldChild);
182 }
183
184 RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type)
185 {
186 if (type == RenderStyle::BEFORE) {
187 RenderObject* first = this;
188 do {
189 // Skip list markers.
190 first = first->firstChild();
191 while (first && first->isListMarker())
192 first = first->nextSibling();
193 } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO);
194 if (first && first->style()->styleType() != type)
195 return 0;
196 return first;
197 }
198 if (type == RenderStyle::AFTER) {
199 RenderObject* last = this;
200 do {
201 last = last->lastChild();
202 } while (last && last->isAnonymous() && last->style()->styleType() == Re nderStyle::NOPSEUDO && !last->isListMarker());
203 if (last && last->style()->styleType() != type)
204 return 0;
205 return last;
206 }
207
208 ASSERT_NOT_REACHED();
209 return 0;
210 }
211
212 void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type)
213 {
214 // If this is an anonymous wrapper, then the parent applies its own pseudo-e lement style to it.
215 if (parent() && parent()->createsAnonymousWrapper())
216 return;
217 updateBeforeAfterContentForContainer(type, this);
218 }
219
220 static RenderObject* findBeforeAfterParent(RenderObject* object)
221 {
222 // Only table parts need to search for the :before or :after parent
223 if (!(object->isTable() || object->isTableSection() || object->isTableRow()) )
224 return object;
225
226 RenderObject* beforeAfterParent = object;
227 while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterPare nt->isImage()))
228 beforeAfterParent = beforeAfterParent->firstChild();
229 return beforeAfterParent;
230 }
231
232 void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject)
233 {
234 // Double check that the document did in fact use generated content rules. Otherwise we should not have been called.
235 ASSERT(document()->usesBeforeAfterRules());
236
237 // In CSS2, before/after pseudo-content cannot nest. Check this first.
238 if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == R enderStyle::AFTER)
239 return;
240
241 RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type);
242 RenderObject* child = beforeAfterContainer(type);
243
244 // Whether or not we currently have generated content attached.
245 bool oldContentPresent = child;
246
247 // Whether or not we now want generated content.
248 bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE;
249
250 // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
251 // :after content and not :before content.
252 if (newContentWanted && type == RenderStyle::BEFORE && isRenderInline() && t oRenderInline(this)->isInlineContinuation())
253 newContentWanted = false;
254
255 // Similarly, if we're the beginning of a <q>, and there's an inline continu ation for our object,
256 // then we don't generate the :after content.
257 if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && to RenderInline(this)->continuation())
258 newContentWanted = false;
259
260 // If we don't want generated content any longer, or if we have generated co ntent, but it's no longer
261 // identical to the new content data we want to build render objects for, th en we nuke all
262 // of the old generated content.
263 if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), ps eudoElementStyle) == Node::Detach)) {
264 // Nuke the child.
265 if (child && child->style()->styleType() == type) {
266 oldContentPresent = false;
267 child->destroy();
268 child = (type == RenderStyle::BEFORE) ? children()->firstChild() : c hildren()->lastChild();
269 }
270 }
271
272 // If we have no pseudo-element style or if the pseudo-element style's displ ay type is NONE, then we
273 // have no generated content and can now return.
274 if (!newContentWanted)
275 return;
276
277 if (isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudo ElementStyle->floating() == FNONE &&
278 !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementSty le->position() == FixedPosition))
279 // According to the CSS2 spec (the end of section 12.1), the only allowe d
280 // display values for the pseudo style are NONE and INLINE for inline fl ows.
281 // FIXME: CSS2.1 lifted this restriction, but block display types will c rash.
282 // For now we at least relax the restriction to allow all inline types l ike inline-block
283 // and inline-table.
284 pseudoElementStyle->setDisplay(INLINE);
285
286 if (oldContentPresent) {
287 if (child && child->style()->styleType() == type) {
288 // We have generated content present still. We want to walk this co ntent and update our
289 // style information with the new pseudo-element style.
290 child->setStyle(pseudoElementStyle);
291
292 RenderObject* beforeAfterParent = findBeforeAfterParent(child);
293 if (!beforeAfterParent)
294 return;
295
296 // Note that if we ever support additional types of generated conten t (which should be way off
297 // in the future), this code will need to be patched.
298 for (RenderObject* genChild = beforeAfterParent->firstChild(); genCh ild; genChild = genChild->nextSibling()) {
299 if (genChild->isText())
300 // Generated text content is a child whose style also needs to be set to the pseudo-element style.
301 genChild->setStyle(pseudoElementStyle);
302 else if (genChild->isImage()) {
303 // Images get an empty style that inherits from the pseudo.
304 RefPtr<RenderStyle> style = RenderStyle::create();
305 style->inheritFrom(pseudoElementStyle);
306 genChild->setStyle(style.release());
307 } else
308 // Must be a first-letter container. updateFirstLetter() wil l take care of it.
309 ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_ LETTER);
310 }
311 }
312 return; // We've updated the generated content. That's all we needed to do.
313 }
314
315 RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? children()->fir stChild() : 0;
316
317 // Generated content consists of a single container that houses multiple chi ldren (specified
318 // by the content property). This generated content container gets the pseu do-element style set on it.
319 RenderObject* generatedContentContainer = 0;
320
321 // Walk our list of generated content and create render objects for each.
322 for (const ContentData* content = pseudoElementStyle->contentData(); content ; content = content->m_next) {
323 RenderObject* renderer = 0;
324 switch (content->m_type) {
325 case CONTENT_NONE:
326 break;
327 case CONTENT_TEXT:
328 renderer = new (renderArena()) RenderTextFragment(document() /* anonymous object */, content->m_content.m_text);
329 renderer->setStyle(pseudoElementStyle);
330 break;
331 case CONTENT_OBJECT: {
332 RenderImageGeneratedContent* image = new (renderArena()) RenderI mageGeneratedContent(document()); // anonymous object
333 RefPtr<RenderStyle> style = RenderStyle::create();
334 style->inheritFrom(pseudoElementStyle);
335 image->setStyle(style.release());
336 if (StyleImage* styleImage = content->m_content.m_image)
337 image->setStyleImage(styleImage);
338 renderer = image;
339 break;
340 }
341 case CONTENT_COUNTER:
342 renderer = new (renderArena()) RenderCounter(document(), *conten t->m_content.m_counter);
343 renderer->setStyle(pseudoElementStyle);
344 break;
345 }
346
347 if (renderer) {
348 if (!generatedContentContainer) {
349 // Make a generated box that might be any display type now that we are able to drill down into children
350 // to find the original content properly.
351 generatedContentContainer = RenderObject::createObject(document( ), pseudoElementStyle);
352 generatedContentContainer->setStyle(pseudoElementStyle);
353 addChild(generatedContentContainer, insertBefore);
354 }
355 generatedContentContainer->addChild(renderer);
356 }
357 }
358 }
359
360 bool RenderContainer::isAfterContent(RenderObject* child) const
361 {
362 if (!child)
363 return false;
364 if (child->style()->styleType() != RenderStyle::AFTER)
365 return false;
366 // Text nodes don't have their own styles, so ignore the style on a text nod e.
367 if (child->isText() && !child->isBR())
368 return false;
369 return true;
370 }
371
372 static void invalidateCountersInContainer(RenderObject* container)
373 {
374 if (!container)
375 return;
376 container = findBeforeAfterParent(container);
377 if (!container)
378 return;
379 for (RenderObject* content = container->firstChild(); content; content = con tent->nextSibling()) {
380 if (content->isCounter())
381 static_cast<RenderCounter*>(content)->invalidate();
382 }
383 }
384
385 void RenderContainer::invalidateCounters()
386 {
387 if (documentBeingDestroyed())
388 return;
389
390 invalidateCountersInContainer(beforeAfterContainer(RenderStyle::BEFORE));
391 invalidateCountersInContainer(beforeAfterContainer(RenderStyle::AFTER));
392 }
393
394 void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend)
395 {
396 ASSERT(newChild->parent() == 0);
397 ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableR ow() && !newChild->isTableCell()));
398
399 newChild->setParent(this);
400 RenderObject* lChild = children()->lastChild();
401
402 if (lChild) {
403 newChild->setPreviousSibling(lChild);
404 lChild->setNextSibling(newChild);
405 } else
406 children()->setFirstChild(newChild);
407
408 children()->setLastChild(newChild);
409
410 if (fullAppend) {
411 // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
412 // and don't have a layer attached to ourselves.
413 RenderLayer* layer = 0;
414 if (newChild->firstChild() || newChild->hasLayer()) {
415 layer = enclosingLayer();
416 newChild->addLayers(layer, newChild);
417 }
418
419 // if the new child is visible but this object was not, tell the layer i t has some visible content
420 // that needs to be drawn and layer visibility optimization can't be use d
421 if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) {
422 if (!layer)
423 layer = enclosingLayer();
424 if (layer)
425 layer->setHasVisibleContent(true);
426 }
427
428 if (!newChild->isFloatingOrPositioned() && childrenInline())
429 dirtyLinesFromChangedChild(newChild);
430 }
431
432 newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing blo ck hierarchy.
433 if (!normalChildNeedsLayout())
434 setChildNeedsLayout(true); // We may supply the static position for an a bsolute positioned child.
435
436 if (AXObjectCache::accessibilityEnabled())
437 document()->axObjectCache()->childrenChanged(this);
438 }
439
440 void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeC hild, bool fullInsert)
441 {
442 if (!beforeChild) {
443 appendChildNode(child);
444 return;
445 }
446
447 ASSERT(!child->parent());
448 while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBl ock())
449 beforeChild = beforeChild->parent();
450 ASSERT(beforeChild->parent() == this);
451
452 ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() & & !child->isTableCell()));
453
454 if (beforeChild == children()->firstChild())
455 children()->setFirstChild(child);
456
457 RenderObject* prev = beforeChild->previousSibling();
458 child->setNextSibling(beforeChild);
459 beforeChild->setPreviousSibling(child);
460 if(prev) prev->setNextSibling(child);
461 child->setPreviousSibling(prev);
462
463 child->setParent(this);
464
465 if (fullInsert) {
466 // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
467 // and don't have a layer attached to ourselves.
468 RenderLayer* layer = 0;
469 if (child->firstChild() || child->hasLayer()) {
470 layer = enclosingLayer();
471 child->addLayers(layer, child);
472 }
473
474 // if the new child is visible but this object was not, tell the layer i t has some visible content
475 // that needs to be drawn and layer visibility optimization can't be use d
476 if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) {
477 if (!layer)
478 layer = enclosingLayer();
479 if (layer)
480 layer->setHasVisibleContent(true);
481 }
482
483
484 if (!child->isFloating() && childrenInline())
485 dirtyLinesFromChangedChild(child);
486 }
487
488 child->setNeedsLayoutAndPrefWidthsRecalc();
489 if (!normalChildNeedsLayout())
490 setChildNeedsLayout(true); // We may supply the static position for an a bsolute positioned child.
491
492 if (AXObjectCache::accessibilityEnabled())
493 document()->axObjectCache()->childrenChanged(this);
494 }
495
496 VisiblePosition RenderContainer::positionForCoordinates(int xPos, int yPos)
497 {
498 // no children...return this render object's element, if there is one, and o ffset 0
499 if (!children()->firstChild())
500 return VisiblePosition(element(), 0, DOWNSTREAM);
501
502 if (isTable() && element()) {
503 int right = contentWidth() + borderRight() + paddingRight() + borderLeft () + paddingLeft();
504 int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom () + paddingBottom();
505
506 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
507 if (xPos <= right / 2)
508 return VisiblePosition(Position(element(), 0));
509 else
510 return VisiblePosition(Position(element(), maxDeepOffset(element ())));
511 }
512 }
513
514 // Pass off to the closest child.
515 int minDist = INT_MAX;
516 RenderBox* closestRenderer = 0;
517 int newX = xPos;
518 int newY = yPos;
519 if (isTableRow()) {
520 newX += x();
521 newY += y();
522 }
523 for (RenderObject* renderObject = children()->firstChild(); renderObject; re nderObject = renderObject->nextSibling()) {
524 if (!renderObject->firstChild() && !renderObject->isInline() && !renderO bject->isBlockFlow()
525 || renderObject->style()->visibility() != VISIBLE)
526 continue;
527
528 if (!renderObject->isBox())
529 continue;
530
531 RenderBox* renderer = toRenderBox(renderObject);
532
533 int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->y() );
534 int bottom = top + renderer->contentHeight();
535 int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer-> x());
536 int right = left + renderer->contentWidth();
537
538 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
539 if (renderer->isTableRow())
540 return renderer->positionForCoordinates(xPos + newX - renderer-> x(), yPos + newY - renderer->y());
541 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
542 }
543
544 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
545 // and use a different compare depending on which piece (x, y) is in.
546 IntPoint cmp;
547 if (xPos > right) {
548 if (yPos < top)
549 cmp = IntPoint(right, top);
550 else if (yPos > bottom)
551 cmp = IntPoint(right, bottom);
552 else
553 cmp = IntPoint(right, yPos);
554 } else if (xPos < left) {
555 if (yPos < top)
556 cmp = IntPoint(left, top);
557 else if (yPos > bottom)
558 cmp = IntPoint(left, bottom);
559 else
560 cmp = IntPoint(left, yPos);
561 } else {
562 if (yPos < top)
563 cmp = IntPoint(xPos, top);
564 else
565 cmp = IntPoint(xPos, bottom);
566 }
567
568 int x1minusx2 = cmp.x() - xPos;
569 int y1minusy2 = cmp.y() - yPos;
570
571 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
572 if (dist < minDist) {
573 closestRenderer = renderer;
574 minDist = dist;
575 }
576 }
577
578 if (closestRenderer)
579 return closestRenderer->positionForCoordinates(newX - closestRenderer->x (), newY - closestRenderer->y());
580
581 return VisiblePosition(element(), 0, DOWNSTREAM);
582 } 116 }
583 117
584 void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, un signed end, bool) 118 void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, un signed end, bool)
585 { 119 {
586 if (!children()->firstChild() && (isInline() || isAnonymousBlock())) { 120 if (!children()->firstChild() && (isInline() || isAnonymousBlock())) {
587 FloatPoint absPos = localToAbsolute(FloatPoint()); 121 FloatPoint absPos = localToAbsolute(FloatPoint());
588 absoluteRects(rects, absPos.x(), absPos.y()); 122 absoluteRects(rects, absPos.x(), absPos.y());
589 return; 123 return;
590 } 124 }
591 125
(...skipping 22 matching lines...) Expand all
614 unsigned offset = start; 148 unsigned offset = start;
615 for (RenderObject* child = childAt(start); child && offset < end; child = ch ild->nextSibling(), ++offset) { 149 for (RenderObject* child = childAt(start); child && offset < end; child = ch ild->nextSibling(), ++offset) {
616 if (child->isText() || child->isInline() || child->isAnonymousBlock()) 150 if (child->isText() || child->isInline() || child->isAnonymousBlock())
617 child->absoluteQuads(quads); 151 child->absoluteQuads(quads);
618 } 152 }
619 } 153 }
620 154
621 #undef DEBUG_LAYOUT 155 #undef DEBUG_LAYOUT
622 156
623 } // namespace WebCore 157 } // namespace WebCore
OLDNEW
« no previous file with comments | « third_party/WebKit/WebCore/rendering/RenderContainer.h ('k') | third_party/WebKit/WebCore/rendering/RenderCounter.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698