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

Side by Side Diff: Source/core/paint/BoxPainter.cpp

Issue 1033943002: Rename LayoutStyle to papayawhip (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: ensureComputedStyle Created 5 years, 8 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
« no previous file with comments | « Source/core/paint/BoxPainter.h ('k') | Source/core/paint/DeprecatedPaintLayer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 #include "core/paint/BoxPainter.h" 6 #include "core/paint/BoxPainter.h"
7 7
8 #include "core/HTMLNames.h" 8 #include "core/HTMLNames.h"
9 #include "core/frame/Settings.h" 9 #include "core/frame/Settings.h"
10 #include "core/html/HTMLFrameOwnerElement.h" 10 #include "core/html/HTMLFrameOwnerElement.h"
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 result.move(-layoutView->scrolledContentOffset()); 74 result.move(-layoutView->scrolledContentOffset());
75 return result; 75 return result;
76 } 76 }
77 77
78 void BoxPainter::paintBoxDecorationBackgroundWithRect(const PaintInfo& paintInfo , const LayoutPoint& paintOffset, const LayoutRect& paintRect) 78 void BoxPainter::paintBoxDecorationBackgroundWithRect(const PaintInfo& paintInfo , const LayoutPoint& paintOffset, const LayoutRect& paintRect)
79 { 79 {
80 LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutBox, Display Item::BoxDecorationBackground, boundsForDrawingRecorder(paintOffset)); 80 LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutBox, Display Item::BoxDecorationBackground, boundsForDrawingRecorder(paintOffset));
81 if (recorder.canUseCachedDrawing()) 81 if (recorder.canUseCachedDrawing())
82 return; 82 return;
83 83
84 const LayoutStyle& style = m_layoutBox.styleRef(); 84 const ComputedStyle& style = m_layoutBox.styleRef();
85 BoxDecorationData boxDecorationData(m_layoutBox, paintInfo.context); 85 BoxDecorationData boxDecorationData(m_layoutBox, paintInfo.context);
86 86
87 // FIXME: Should eventually give the theme control over whether the box shad ow should paint, since controls could have 87 // FIXME: Should eventually give the theme control over whether the box shad ow should paint, since controls could have
88 // custom shadows of their own. 88 // custom shadows of their own.
89 if (!m_layoutBox.boxShadowShouldBeAppliedToBackground(boxDecorationData.blee dAvoidance())) 89 if (!m_layoutBox.boxShadowShouldBeAppliedToBackground(boxDecorationData.blee dAvoidance()))
90 paintBoxShadow(paintInfo, paintRect, style, Normal); 90 paintBoxShadow(paintInfo, paintRect, style, Normal);
91 91
92 GraphicsContextStateSaver stateSaver(*paintInfo.context, false); 92 GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
93 if (boxDecorationData.bleedAvoidance() == BackgroundBleedClipBackground) { 93 if (boxDecorationData.bleedAvoidance() == BackgroundBleedClipBackground) {
94 stateSaver.save(); 94 stateSaver.save();
(...skipping 792 matching lines...) Expand 10 before | Expand all | Expand 10 after
887 887
888 static LayoutUnit computeBorderImageSide(const BorderImageLength& borderSlice, L ayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent) 888 static LayoutUnit computeBorderImageSide(const BorderImageLength& borderSlice, L ayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent)
889 { 889 {
890 if (borderSlice.isNumber()) 890 if (borderSlice.isNumber())
891 return borderSlice.number() * borderSide; 891 return borderSlice.number() * borderSide;
892 if (borderSlice.length().isAuto()) 892 if (borderSlice.length().isAuto())
893 return imageSide; 893 return imageSide;
894 return valueForLength(borderSlice.length(), boxExtent); 894 return valueForLength(borderSlice.length(), boxExtent);
895 } 895 }
896 896
897 bool BoxPainter::paintNinePieceImage(LayoutBoxModelObject& obj, GraphicsContext* graphicsContext, const LayoutRect& rect, const LayoutStyle& style, const NinePi eceImage& ninePieceImage, SkXfermode::Mode op) 897 bool BoxPainter::paintNinePieceImage(LayoutBoxModelObject& obj, GraphicsContext* graphicsContext, const LayoutRect& rect, const ComputedStyle& style, const Nine PieceImage& ninePieceImage, SkXfermode::Mode op)
898 { 898 {
899 StyleImage* styleImage = ninePieceImage.image(); 899 StyleImage* styleImage = ninePieceImage.image();
900 if (!styleImage) 900 if (!styleImage)
901 return false; 901 return false;
902 902
903 if (!styleImage->isLoaded()) 903 if (!styleImage->isLoaded())
904 return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. 904 return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
905 905
906 if (!styleImage->canRender(obj, style.effectiveZoom())) 906 if (!styleImage->canRender(obj, style.effectiveZoom()))
907 return false; 907 return false;
(...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after
1394 context->setShouldAntialias(antialias); 1394 context->setShouldAntialias(antialias);
1395 1395
1396 context->setStrokeStyle(SolidStroke); 1396 context->setStrokeStyle(SolidStroke);
1397 context->setStrokeColor(color); 1397 context->setStrokeColor(color);
1398 context->strokeRect(strokeRect, borderWidth); 1398 context->strokeRect(strokeRect, borderWidth);
1399 1399
1400 if (antialias != wasAntialias) 1400 if (antialias != wasAntialias)
1401 context->setShouldAntialias(wasAntialias); 1401 context->setShouldAntialias(wasAntialias);
1402 } 1402 }
1403 1403
1404 void BoxPainter::paintBorder(LayoutBoxModelObject& obj, const PaintInfo& info, c onst LayoutRect& rect, const LayoutStyle& style, BackgroundBleedAvoidance bleedA voidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 1404 void BoxPainter::paintBorder(LayoutBoxModelObject& obj, const PaintInfo& info, c onst LayoutRect& rect, const ComputedStyle& style, BackgroundBleedAvoidance blee dAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
1405 { 1405 {
1406 GraphicsContext* graphicsContext = info.context; 1406 GraphicsContext* graphicsContext = info.context;
1407 // border-image is not affected by border-radius. 1407 // border-image is not affected by border-radius.
1408 if (paintNinePieceImage(obj, graphicsContext, rect, style, style.borderImage ())) 1408 if (paintNinePieceImage(obj, graphicsContext, rect, style, style.borderImage ()))
1409 return; 1409 return;
1410 1410
1411 BorderEdge edges[4]; 1411 BorderEdge edges[4];
1412 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEd ge); 1412 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEd ge);
1413 FloatRoundedRect outerBorder = style.getRoundedBorderFor(rect, includeLogica lLeftEdge, includeLogicalRightEdge); 1413 FloatRoundedRect outerBorder = style.getRoundedBorderFor(rect, includeLogica lLeftEdge, includeLogicalRightEdge);
1414 FloatRoundedRect innerBorder = style.getRoundedInnerBorderFor(borderInnerRec tAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogica lLeftEdge, includeLogicalRightEdge); 1414 FloatRoundedRect innerBorder = style.getRoundedInnerBorderFor(borderInnerRec tAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogica lLeftEdge, includeLogicalRightEdge);
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
1584 } 1584 }
1585 1585
1586 static inline bool includesAdjacentEdges(BorderEdgeFlags flags) 1586 static inline bool includesAdjacentEdges(BorderEdgeFlags flags)
1587 { 1587 {
1588 return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | Right BorderEdge) 1588 return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | Right BorderEdge)
1589 || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge) 1589 || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge)
1590 || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge) 1590 || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge)
1591 || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBo rderEdge); 1591 || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBo rderEdge);
1592 } 1592 }
1593 1593
1594 void BoxPainter::paintTranslucentBorderSides(GraphicsContext* graphicsContext, c onst LayoutStyle& style, const FloatRoundedRect& outerBorder, const FloatRounded Rect& innerBorder, const IntPoint& innerBorderAdjustment, 1594 void BoxPainter::paintTranslucentBorderSides(GraphicsContext* graphicsContext, c onst ComputedStyle& style, const FloatRoundedRect& outerBorder, const FloatRound edRect& innerBorder, const IntPoint& innerBorderAdjustment,
1595 const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoida nce bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, b ool antialias) 1595 const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoida nce bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, b ool antialias)
1596 { 1596 {
1597 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. 1597 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right.
1598 // This is different from BoxSide enum order. 1598 // This is different from BoxSide enum order.
1599 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; 1599 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight };
1600 1600
1601 while (edgesToDraw) { 1601 while (edgesToDraw) {
1602 // Find undrawn edges sharing a color. 1602 // Find undrawn edges sharing a color.
1603 Color commonColor; 1603 Color commonColor;
1604 1604
(...skipping 30 matching lines...) Expand all
1635 edgesToDraw &= ~commonColorEdgeSet; 1635 edgesToDraw &= ~commonColorEdgeSet;
1636 } 1636 }
1637 } 1637 }
1638 1638
1639 LayoutRect BoxPainter::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) 1639 LayoutRect BoxPainter::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance)
1640 { 1640 {
1641 // We shrink the rectangle by one pixel on each side to make it fully overla p the anti-aliased background border 1641 // We shrink the rectangle by one pixel on each side to make it fully overla p the anti-aliased background border
1642 return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectB yOnePixel(context, rect) : rect; 1642 return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectB yOnePixel(context, rect) : rect;
1643 } 1643 }
1644 1644
1645 void BoxPainter::paintOneBorderSide(GraphicsContext* graphicsContext, const Layo utStyle& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& inn erBorder, 1645 void BoxPainter::paintOneBorderSide(GraphicsContext* graphicsContext, const Comp utedStyle& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& i nnerBorder,
1646 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2, const BorderEdge edges[], const Path* path, 1646 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2, const BorderEdge edges[], const Path* path,
1647 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i ncludeLogicalRightEdge, bool antialias, const Color* overrideColor) 1647 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i ncludeLogicalRightEdge, bool antialias, const Color* overrideColor)
1648 { 1648 {
1649 const BorderEdge& edgeToRender = edges[side]; 1649 const BorderEdge& edgeToRender = edges[side];
1650 ASSERT(edgeToRender.width); 1650 ASSERT(edgeToRender.width);
1651 const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; 1651 const BorderEdge& adjacentEdge1 = edges[adjacentSide1];
1652 const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; 1652 const BorderEdge& adjacentEdge2 = edges[adjacentSide2];
1653 1653
1654 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !ant ialias); 1654 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !ant ialias);
1655 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !ant ialias); 1655 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !ant ialias);
(...skipping 26 matching lines...) Expand all
1682 // Since we clipped, no need to draw with a mitre. 1682 // Since we clipped, no need to draw with a mitre.
1683 mitreAdjacentSide1 = false; 1683 mitreAdjacentSide1 = false;
1684 mitreAdjacentSide2 = false; 1684 mitreAdjacentSide2 = false;
1685 } 1685 }
1686 1686
1687 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.border Style(), 1687 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.border Style(),
1688 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a djacentEdge2.width : 0, antialias); 1688 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a djacentEdge2.width : 0, antialias);
1689 } 1689 }
1690 } 1690 }
1691 1691
1692 void BoxPainter::paintBorderSides(GraphicsContext* graphicsContext, const Layout Style& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& inner Border, 1692 void BoxPainter::paintBorderSides(GraphicsContext* graphicsContext, const Comput edStyle& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& inn erBorder,
1693 const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeF lags edgeSet, BackgroundBleedAvoidance bleedAvoidance, 1693 const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeF lags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
1694 bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, c onst Color* overrideColor) 1694 bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, c onst Color* overrideColor)
1695 { 1695 {
1696 bool renderRadii = outerBorder.isRounded(); 1696 bool renderRadii = outerBorder.isRounded();
1697 1697
1698 Path roundedPath; 1698 Path roundedPath;
1699 if (renderRadii) 1699 if (renderRadii)
1700 roundedPath.addRoundedRect(outerBorder); 1700 roundedPath.addRoundedRect(outerBorder);
1701 1701
1702 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg roundOverBorder 1702 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg roundOverBorder
(...skipping 28 matching lines...) Expand all
1731 if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { 1731 if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
1732 FloatRect sideRect = outerBorder.rect(); 1732 FloatRect sideRect = outerBorder.rect();
1733 sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBord erAdjustment.x()); 1733 sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBord erAdjustment.x());
1734 1734
1735 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight]. borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), inne rBorder.radii().topRight())); 1735 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight]. borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), inne rBorder.radii().topRight()));
1736 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid eRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidan ce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); 1736 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid eRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidan ce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
1737 } 1737 }
1738 } 1738 }
1739 1739
1740 void BoxPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, const Lay outRect& borderRect, const Path& borderPath, const BorderEdge edges[], 1740 void BoxPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, const Lay outRect& borderRect, const Path& borderPath, const BorderEdge edges[],
1741 float thickness, float drawThickness, BoxSide side, const LayoutStyle& style , Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance , 1741 float thickness, float drawThickness, BoxSide side, const ComputedStyle& sty le, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidan ce,
1742 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 1742 bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
1743 { 1743 {
1744 if (thickness <= 0) 1744 if (thickness <= 0)
1745 return; 1745 return;
1746 1746
1747 if (borderStyle == DOUBLE && thickness < 3) 1747 if (borderStyle == DOUBLE && thickness < 3)
1748 borderStyle = SOLID; 1748 borderStyle = SOLID;
1749 1749
1750 switch (borderStyle) { 1750 switch (borderStyle) {
1751 case BNONE: 1751 case BNONE:
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
1882 break; 1882 break;
1883 default: 1883 default:
1884 break; 1884 break;
1885 } 1885 }
1886 1886
1887 graphicsContext->setStrokeStyle(NoStroke); 1887 graphicsContext->setStrokeStyle(NoStroke);
1888 graphicsContext->setFillColor(color); 1888 graphicsContext->setFillColor(color);
1889 graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); 1889 graphicsContext->drawRect(pixelSnappedIntRect(borderRect));
1890 } 1890 }
1891 1891
1892 void BoxPainter::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRe ct, const LayoutStyle& style, ShadowStyle shadowStyle, bool includeLogicalLeftEd ge, bool includeLogicalRightEdge) 1892 void BoxPainter::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRe ct, const ComputedStyle& style, ShadowStyle shadowStyle, bool includeLogicalLeft Edge, bool includeLogicalRightEdge)
1893 { 1893 {
1894 // FIXME: Deal with border-image. Would be great to use border-image as a ma sk. 1894 // FIXME: Deal with border-image. Would be great to use border-image as a ma sk.
1895 GraphicsContext* context = info.context; 1895 GraphicsContext* context = info.context;
1896 if (!style.boxShadow()) 1896 if (!style.boxShadow())
1897 return; 1897 return;
1898 FloatRoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBord erFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) 1898 FloatRoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBord erFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
1899 : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLo gicalRightEdge); 1899 : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLo gicalRightEdge);
1900 1900
1901 bool hasBorderRadius = style.hasBorderRadius(); 1901 bool hasBorderRadius = style.hasBorderRadius();
1902 bool isHorizontal = style.isHorizontalWritingMode(); 1902 bool isHorizontal = style.isHorizontalWritingMode();
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
2202 2202
2203 FloatPoint secondQuad[4]; 2203 FloatPoint secondQuad[4];
2204 secondQuad[0] = quad[0]; 2204 secondQuad[0] = quad[0];
2205 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); 2205 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy);
2206 secondQuad[2] = quad[2]; 2206 secondQuad[2] = quad[2];
2207 secondQuad[3] = quad[3]; 2207 secondQuad[3] = quad[3];
2208 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); 2208 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches);
2209 } 2209 }
2210 2210
2211 } // namespace blink 2211 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/paint/BoxPainter.h ('k') | Source/core/paint/DeprecatedPaintLayer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698