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

Side by Side Diff: Source/core/rendering/RenderBlock.cpp

Issue 249353003: Apply the correct style to first-letter pseudo elements composed of different renderers (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 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
« no previous file with comments | « LayoutTests/fast/css/first-letter-punctuation-expected.html ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) 2007 David Smith (catfish.man@gmail.com) 4 * (C) 2007 David Smith (catfish.man@gmail.com)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
7 * 8 *
8 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public 10 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either 11 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version. 12 * version 2 of the License, or (at your option) any later version.
12 * 13 *
13 * This library is distributed in the hope that it will be useful, 14 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details. 17 * Library General Public License for more details.
(...skipping 3842 matching lines...) Expand 10 before | Expand all | Expand 10 after
3859 static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderOb ject* firstLetterContainer) 3860 static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderOb ject* firstLetterContainer)
3860 { 3861 {
3861 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETT ER, firstLetterContainer->firstLineStyle()); 3862 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETT ER, firstLetterContainer->firstLineStyle());
3862 // Force inline display (except for floating first-letters). 3863 // Force inline display (except for floating first-letters).
3863 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); 3864 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
3864 // CSS2 says first-letter can't be positioned. 3865 // CSS2 says first-letter can't be positioned.
3865 pseudoStyle->setPosition(StaticPosition); 3866 pseudoStyle->setPosition(StaticPosition);
3866 return pseudoStyle; 3867 return pseudoStyle;
3867 } 3868 }
3868 3869
3869 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
3870 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps) , "close" (Pe),
3871 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that prec edes or follows the first letter should be included"
3872 static inline bool isPunctuationForFirstLetter(UChar c)
3873 {
3874 CharCategory charCategory = category(c);
3875 return charCategory == Punctuation_Open
3876 || charCategory == Punctuation_Close
3877 || charCategory == Punctuation_InitialQuote
3878 || charCategory == Punctuation_FinalQuote
3879 || charCategory == Punctuation_Other;
3880 }
3881
3882 static inline bool isSpaceForFirstLetter(UChar c)
3883 {
3884 return isSpaceOrNewline(c) || c == noBreakSpace;
3885 }
3886
3887 static inline RenderObject* findFirstLetterBlock(RenderBlock* start) 3870 static inline RenderObject* findFirstLetterBlock(RenderBlock* start)
3888 { 3871 {
3889 RenderObject* firstLetterBlock = start; 3872 RenderObject* firstLetterBlock = start;
3890 while (true) { 3873 while (true) {
3891 // We include isRenderButton in these two checks because buttons are 3874 // We include isRenderButton in these two checks because buttons are
3892 // implemented using flex box but should still support first-letter. 3875 // implemented using flex box but should still support first-letter.
3893 // The flex box spec requires that flex box does not support 3876 // The flex box spec requires that flex box does not support
3894 // first-letter, though. 3877 // first-letter, though.
3895 // FIXME: Remove when buttons are implemented with align-items instead 3878 // FIXME: Remove when buttons are implemented with align-items instead
3896 // of flexbox. 3879 // of flexbox.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
3950 firstLetterContainer->addChild(firstLetter, nextSibling); 3933 firstLetterContainer->addChild(firstLetter, nextSibling);
3951 } else 3934 } else
3952 firstLetter->setStyle(pseudoStyle); 3935 firstLetter->setStyle(pseudoStyle);
3953 3936
3954 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) { 3937 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
3955 if (genChild->isText()) 3938 if (genChild->isText())
3956 genChild->setStyle(pseudoStyle); 3939 genChild->setStyle(pseudoStyle);
3957 } 3940 }
3958 } 3941 }
3959 3942
3960 static inline unsigned firstLetterLength(const String& text)
3961 {
3962 unsigned length = 0;
3963 unsigned textLength = text.length();
3964
3965 // Account for leading spaces first.
3966 while (length < textLength && isSpaceForFirstLetter(text[length]))
3967 length++;
3968
3969 // Now account for leading punctuation.
3970 while (length < textLength && isPunctuationForFirstLetter(text[length]))
3971 length++;
3972
3973 // Bail if we didn't find a letter before the end of the text or before a sp ace.
3974 if (isSpaceForFirstLetter(text[length]) || (textLength && length == textLeng th))
3975 return 0;
3976
3977 // Account the next character for first letter.
3978 length++;
3979
3980 // Keep looking allowed punctuation for the :first-letter.
3981 for (unsigned scanLength = length; scanLength < textLength; ++scanLength) {
3982 UChar c = text[scanLength];
3983
3984 if (!isPunctuationForFirstLetter(c))
3985 break;
3986
3987 length = scanLength + 1;
3988 }
3989
3990 // FIXME: If textLength is 0, length may still be 1!
3991 return length;
3992 }
3993
3994 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend erObject* currentChild, unsigned length) 3943 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend erObject* currentChild, unsigned length)
3995 { 3944 {
3996 ASSERT(length && currentChild->isText()); 3945 ASSERT(length && currentChild->isText());
3997 3946
3998 RenderObject* firstLetterContainer = currentChild->parent(); 3947 RenderObject* firstLetterContainer = currentChild->parent();
3999 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetter Container); 3948 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetter Container);
4000 RenderObject* firstLetter = 0; 3949 RenderObject* firstLetter = 0;
4001 if (pseudoStyle->display() == INLINE) 3950 if (pseudoStyle->display() == INLINE)
4002 firstLetter = RenderInline::createAnonymous(&document()); 3951 firstLetter = RenderInline::createAnonymous(&document());
4003 else 3952 else
(...skipping 24 matching lines...) Expand all
4028 3977
4029 // construct text fragment for the first letter 3978 // construct text fragment for the first letter
4030 RenderTextFragment* letter = 3979 RenderTextFragment* letter =
4031 new RenderTextFragment(remainingText->node() ? remainingText->node() : & remainingText->document(), oldText.impl(), 0, length); 3980 new RenderTextFragment(remainingText->node() ? remainingText->node() : & remainingText->document(), oldText.impl(), 0, length);
4032 letter->setStyle(pseudoStyle); 3981 letter->setStyle(pseudoStyle);
4033 firstLetter->addChild(letter); 3982 firstLetter->addChild(letter);
4034 3983
4035 textObj->destroy(); 3984 textObj->destroy();
4036 } 3985 }
4037 3986
3987 static bool isRendererAllowedForFirstLetter(RenderObject* renderer)
3988 {
3989 // FIXME: This black-list of disallowed RenderText subclasses is fragile.
3990 // Should counter be on this list? What about RenderTextFragment?
3991 return renderer->isText() && !renderer->isBR() && !toRenderText(renderer)->i sWordBreak();
3992 }
3993
3994 typedef Vector<std::pair<RenderObject*, unsigned> > FirstLetterRenderersList;
3995
3996 enum FirstLetterSearchState {
3997 SearchLeadingSpaces,
3998 SearchLeadingPunctuation,
3999 SearchFirstLetterCharacter,
4000 SearchTrailingPunctuation
4001 };
4002
4003 class FirstLetterFinder {
4004 WTF_MAKE_NONCOPYABLE(FirstLetterFinder);
4005 public:
4006 FirstLetterFinder(RenderObject* block)
4007 : m_containerBlock(block)
4008 , m_renderers()
4009 , m_searchState(SearchLeadingSpaces)
4010 , m_firstLetterFound(false)
4011 {
4012 // The purpose of this class is to find the renderers that would be part
4013 // of the first-letter pseudo element, so go find them right now.
4014 findTextRenderers();
4015 }
4016
4017 FirstLetterRenderersList& renderers() { return m_renderers; }
4018 RenderObject* containerBlock() { return m_containerBlock; }
4019
4020 private:
4021 void findTextRenderers()
4022 {
4023 // Drill into inlines looking for the render objects to be transformed i nto first-letter pseudoelements.
4024 RenderObject* currentChild = m_containerBlock->firstChild();
4025 unsigned currentLength = 0;
4026
4027 while (currentChild) {
4028 if (currentChild->isText()) {
4029 // Process the renderer and store it in the list if valid text w as found.
4030 currentLength = processTextRenderer(currentChild);
4031 if (currentLength)
4032 m_renderers.append(std::make_pair(currentChild, currentLengt h));
4033
4034 // No need to keep looking if we haven't found anything with the
4035 // current renderer or if we already made a decision.
4036 String text = rendererTextForFirstLetter(currentChild);
4037 if (!currentLength || currentLength < text.length())
4038 break;
4039
4040 // We need to look the next object traversing the tree in preord er as if the current renderer
4041 // was a leaf node (which probably is anyway) but without leavin g the scope of the parent block.
4042 currentChild = currentChild->nextInPreOrderAfterChildren(m_conta inerBlock);
4043 } else if (currentChild->isListMarker()) {
4044 currentChild = currentChild->nextSibling();
4045 } else if (currentChild->isFloatingOrOutOfFlowPositioned()) {
4046 if (currentChild->style()->styleType() == FIRST_LETTER) {
4047 currentChild = currentChild->firstChild();
4048 if (currentChild) {
4049 // If found a floating/out-of-flow element with the firs t-letter
4050 // style already applied, it means it has been previousl y identified
4051 // and so we should discard whatever we found so far and use that.
4052 m_firstLetterFound = true;
4053 m_renderers.append(std::make_pair(currentChild, renderer TextForFirstLetter(currentChild).length()));
4054 }
4055 break;
4056 }
4057 currentChild = currentChild->nextSibling();
4058 } else if (currentChild->isReplaced() || currentChild->isRenderButto n() || currentChild->isMenuList()) {
4059 break;
4060 } else if (currentChild->style()->hasPseudoStyle(FIRST_LETTER) && cu rrentChild->canHaveGeneratedChildren()) {
4061 // We found a lower-level node with first-letter, which supersed es the higher-level style,
4062 // so we replace the block originally considered as the containe r and empty the list of
4063 // renderers we might have detected so far, as we no longer need to consider those.
4064 m_containerBlock = currentChild;
4065 currentChild = currentChild->firstChild();
4066 m_renderers.clear();
mario.prada 2014/04/23 14:17:48 This line is the main change compared to the patch
4067 } else {
4068 currentChild = currentChild->firstChild();
4069 }
4070 }
4071
4072 if (!m_firstLetterFound) {
4073 // Empty the list of renderers if we did not find a correct set of t hem
4074 // to further generate new elements to apply the first-letter style over.
4075 m_renderers.clear();
4076 }
4077 }
4078
4079 String rendererTextForFirstLetter(RenderObject* renderer) const
4080 {
4081 ASSERT(renderer->isText());
4082 RenderText* textRenderer = toRenderText(renderer);
4083
4084 String result = textRenderer->originalText();
4085 if (!result.isNull())
4086 return result;
4087
4088 if (isRendererAllowedForFirstLetter(renderer))
4089 return textRenderer->text();
4090
4091 return String();
4092 }
4093
4094 unsigned processTextRenderer(RenderObject* renderer)
4095 {
4096 ASSERT(renderer->isText());
4097 String text = rendererTextForFirstLetter(renderer);
4098
4099 // Early return in case we encounter the wrong characters at the beginni ng.
4100 if (text.isEmpty()
4101 || (m_searchState == SearchLeadingPunctuation && isSpaceForFirstLett er(text[0]))
4102 || (m_firstLetterFound && !isPunctuationForFirstLetter(text[0])))
4103 return 0;
4104
4105 // Now start looking for valid characters for the first-letter pseudo el ement.
4106 bool doneSearching = false;
4107 unsigned textLength = text.length();
4108 unsigned length = 0;
4109
4110 while (!doneSearching && length < textLength) {
4111 switch (m_searchState) {
4112 case SearchLeadingSpaces:
4113 advancePositionWhile<isSpaceForFirstLetter>(text, length);
4114 if (length < textLength)
4115 m_searchState = SearchLeadingPunctuation;
4116 break;
4117
4118 case SearchLeadingPunctuation:
4119 advancePositionWhile<isPunctuationForFirstLetter>(text, length);
4120 if (length < textLength)
4121 m_searchState = SearchFirstLetterCharacter;
4122 break;
4123
4124 case SearchFirstLetterCharacter:
4125 // Now spaces are allowed between leading punctuation and the le tter.
4126 if (isSpaceForFirstLetter(text[length]))
4127 return 0;
4128
4129 m_firstLetterFound = true;
4130 m_searchState = SearchTrailingPunctuation;
4131 length++;
4132 break;
4133
4134 case SearchTrailingPunctuation:
4135 for (unsigned scanLength = length; scanLength < textLength; ++sc anLength) {
4136 UChar c = text[scanLength];
4137 if (!isPunctuationForFirstLetter(c)) {
4138 doneSearching = true;
4139 break;
4140 }
4141 length = scanLength + 1;
4142 }
4143 break;
4144 }
4145 }
4146
4147 ASSERT(length <= textLength);
4148 return length;
4149 }
4150
4151 template<bool characterPredicate(UChar)>
4152 void advancePositionWhile(const String& text, unsigned& position)
4153 {
4154 unsigned textLength = text.length();
4155 while (position < textLength && characterPredicate(text[position]))
4156 position++;
4157 }
4158
4159 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
4160 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe),
4161 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included"
4162 static inline bool isPunctuationForFirstLetter(UChar c)
4163 {
4164 CharCategory charCategory = category(c);
4165 return charCategory == Punctuation_Open
4166 || charCategory == Punctuation_Close
4167 || charCategory == Punctuation_InitialQuote
4168 || charCategory == Punctuation_FinalQuote
4169 || charCategory == Punctuation_Other;
4170 }
4171
4172 static inline bool isSpaceForFirstLetter(UChar c)
4173 {
4174 return isSpaceOrNewline(c) || c == noBreakSpace;
4175 }
4176
4177 RenderObject* m_containerBlock;
4178 FirstLetterRenderersList m_renderers;
4179 FirstLetterSearchState m_searchState;
4180 bool m_firstLetterFound;
4181 };
4182
4038 void RenderBlock::updateFirstLetter() 4183 void RenderBlock::updateFirstLetter()
4039 { 4184 {
4040 if (!document().styleEngine()->usesFirstLetterRules()) 4185 if (!document().styleEngine()->usesFirstLetterRules())
4041 return; 4186 return;
4042 // Don't recur 4187
4188 // Early return if the renderer is already known to be part of a first-lette r pseudo element.
4043 if (style()->styleType() == FIRST_LETTER) 4189 if (style()->styleType() == FIRST_LETTER)
4044 return; 4190 return;
4045 4191
4046 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find 4192 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
4047 // an efficient way to check for that situation though before implementing a nything. 4193 // an efficient way to check for that situation though before implementing a nything.
4048 RenderObject* firstLetterBlock = findFirstLetterBlock(this); 4194 RenderObject* firstLetterBlock = findFirstLetterBlock(this);
4049 if (!firstLetterBlock) 4195 if (!firstLetterBlock)
4050 return; 4196 return;
4051 4197
4052 // Drill into inlines looking for our first text child. 4198 // Find the renderers to apply the first-letter style over.
4053 RenderObject* currChild = firstLetterBlock->firstChild(); 4199 FirstLetterFinder firstLetterFinder(firstLetterBlock);
4054 unsigned length = 0; 4200 FirstLetterRenderersList& renderers = firstLetterFinder.renderers();
4055 while (currChild) { 4201 if (renderers.isEmpty())
4056 if (currChild->isText()) {
4057 // FIXME: If there is leading punctuation in a different RenderText than
4058 // the first letter, we'll not apply the correct style to it.
4059 length = firstLetterLength(toRenderText(currChild)->originalText());
4060 if (length)
4061 break;
4062 currChild = currChild->nextSibling();
4063 } else if (currChild->isListMarker()) {
4064 currChild = currChild->nextSibling();
4065 } else if (currChild->isFloatingOrOutOfFlowPositioned()) {
4066 if (currChild->style()->styleType() == FIRST_LETTER) {
4067 currChild = currChild->firstChild();
4068 break;
4069 }
4070 currChild = currChild->nextSibling();
4071 } else if (currChild->isReplaced() || currChild->isRenderButton() || cur rChild->isMenuList())
4072 break;
4073 else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild-> canHaveGeneratedChildren()) {
4074 // We found a lower-level node with first-letter, which supersedes t he higher-level style
4075 firstLetterBlock = currChild;
4076 currChild = currChild->firstChild();
4077 } else
4078 currChild = currChild->firstChild();
4079 }
4080
4081 if (!currChild)
4082 return; 4202 return;
4083 4203
4084 // If the child already has style, then it has already been created, so we j ust want 4204 // The FindLetterFinder might change what considers to be the container bloc k
4085 // to update it. 4205 // for the render objects that form the first-letter pseudo element from the one
4086 if (currChild->parent()->style()->styleType() == FIRST_LETTER) { 4206 // originally passed to the constructor so we need to update the pointer now .
4087 updateFirstLetterStyle(firstLetterBlock, currChild); 4207 firstLetterBlock = firstLetterFinder.containerBlock();
4088 return; 4208
4209 // Create the new renderers for the first-letter pseudo elements.
4210 for (FirstLetterRenderersList::const_iterator it = renderers.begin(); it != renderers.end(); ++it) {
4211 RenderObject* currentRenderer = it->first;
4212 ASSERT(currentRenderer->isText());
4213
4214 // If the child already has style, then it has already been created, so we just want
4215 // to update it.
4216 if (currentRenderer->parent()->style()->styleType() == FIRST_LETTER) {
4217 updateFirstLetterStyle(firstLetterBlock, currentRenderer);
4218 continue;
4219 }
4220
4221 if (!isRendererAllowedForFirstLetter(currentRenderer))
4222 continue;
4223
4224 // Our layout state is not valid for the repaints we are going to trigge r by
4225 // adding and removing children of firstLetterContainer.
4226 LayoutStateDisabler layoutStateDisabler(*this);
4227
4228 unsigned lengthForRenderer = it->second;
4229 if (lengthForRenderer)
4230 createFirstLetterRenderer(firstLetterBlock, currentRenderer, lengthF orRenderer);
4089 } 4231 }
4090
4091 // FIXME: This black-list of disallowed RenderText subclasses is fragile.
4092 // Should counter be on this list? What about RenderTextFragment?
4093 if (!currChild->isText() || currChild->isBR() || toRenderText(currChild)->is WordBreak())
4094 return;
4095
4096 // Our layout state is not valid for the repaints we are going to trigger by
4097 // adding and removing children of firstLetterContainer.
4098 LayoutStateDisabler layoutStateDisabler(*this);
4099
4100 createFirstLetterRenderer(firstLetterBlock, currChild, length);
4101 } 4232 }
4102 4233
4103 // Helper methods for obtaining the last line, computing line counts and heights for line counts 4234 // Helper methods for obtaining the last line, computing line counts and heights for line counts
4104 // (crawling into blocks). 4235 // (crawling into blocks).
4105 static bool shouldCheckLines(RenderObject* obj) 4236 static bool shouldCheckLines(RenderObject* obj)
4106 { 4237 {
4107 return !obj->isFloatingOrOutOfFlowPositioned() 4238 return !obj->isFloatingOrOutOfFlowPositioned()
4108 && obj->isRenderBlock() && obj->style()->height().isAuto() 4239 && obj->isRenderBlock() && obj->style()->height().isAuto()
4109 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERT ICAL); 4240 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERT ICAL);
4110 } 4241 }
(...skipping 843 matching lines...) Expand 10 before | Expand all | Expand 10 after
4954 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* m arkedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const Render Object* obj) const 5085 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* m arkedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const Render Object* obj) const
4955 { 5086 {
4956 showRenderObject(); 5087 showRenderObject();
4957 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRoot Box()) 5088 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRoot Box())
4958 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLa bel2, obj, 1); 5089 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLa bel2, obj, 1);
4959 } 5090 }
4960 5091
4961 #endif 5092 #endif
4962 5093
4963 } // namespace WebCore 5094 } // namespace WebCore
OLDNEW
« no previous file with comments | « LayoutTests/fast/css/first-letter-punctuation-expected.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698