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

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

Issue 207553007: 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: Addressed comments from Eric: refactored code + more testing 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
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 4082 matching lines...) Expand 10 before | Expand all | Expand 10 after
4099 firstLetterContainer->addChild(firstLetter, nextSibling); 4100 firstLetterContainer->addChild(firstLetter, nextSibling);
4100 } else 4101 } else
4101 firstLetter->setStyle(pseudoStyle); 4102 firstLetter->setStyle(pseudoStyle);
4102 4103
4103 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) { 4104 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
4104 if (genChild->isText()) 4105 if (genChild->isText())
4105 genChild->setStyle(pseudoStyle); 4106 genChild->setStyle(pseudoStyle);
4106 } 4107 }
4107 } 4108 }
4108 4109
4109 static inline unsigned firstLetterLength(const String& text)
4110 {
4111 unsigned length = 0;
4112 unsigned textLength = text.length();
4113
4114 // Account for leading spaces first.
4115 while (length < textLength && isSpaceForFirstLetter(text[length]))
4116 length++;
4117
4118 // Now account for leading punctuation.
4119 while (length < textLength && isPunctuationForFirstLetter(text[length]))
4120 length++;
4121
4122 // Bail if we didn't find a letter before the end of the text or before a sp ace.
4123 if (isSpaceForFirstLetter(text[length]) || (textLength && length == textLeng th))
4124 return 0;
4125
4126 // Account the next character for first letter.
4127 length++;
4128
4129 // Keep looking allowed punctuation for the :first-letter.
4130 for (unsigned scanLength = length; scanLength < textLength; ++scanLength) {
4131 UChar c = text[scanLength];
4132
4133 if (!isPunctuationForFirstLetter(c))
4134 break;
4135
4136 length = scanLength + 1;
4137 }
4138
4139 // FIXME: If textLength is 0, length may still be 1!
4140 return length;
4141 }
4142
4143 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend erObject* currentChild, unsigned length) 4110 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend erObject* currentChild, unsigned length)
4144 { 4111 {
4145 ASSERT(length && currentChild->isText()); 4112 ASSERT(length && currentChild->isText());
4146 4113
4147 RenderObject* firstLetterContainer = currentChild->parent(); 4114 RenderObject* firstLetterContainer = currentChild->parent();
4148 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetter Container); 4115 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetter Container);
4149 RenderObject* firstLetter = 0; 4116 RenderObject* firstLetter = 0;
4150 if (pseudoStyle->display() == INLINE) 4117 if (pseudoStyle->display() == INLINE)
4151 firstLetter = RenderInline::createAnonymous(&document()); 4118 firstLetter = RenderInline::createAnonymous(&document());
4152 else 4119 else
(...skipping 24 matching lines...) Expand all
4177 4144
4178 // construct text fragment for the first letter 4145 // construct text fragment for the first letter
4179 RenderTextFragment* letter = 4146 RenderTextFragment* letter =
4180 new RenderTextFragment(remainingText->node() ? remainingText->node() : & remainingText->document(), oldText.impl(), 0, length); 4147 new RenderTextFragment(remainingText->node() ? remainingText->node() : & remainingText->document(), oldText.impl(), 0, length);
4181 letter->setStyle(pseudoStyle); 4148 letter->setStyle(pseudoStyle);
4182 firstLetter->addChild(letter); 4149 firstLetter->addChild(letter);
4183 4150
4184 textObj->destroy(); 4151 textObj->destroy();
4185 } 4152 }
4186 4153
4187 void RenderBlock::updateFirstLetter() 4154 static String rendererTextForFirstLetter(RenderObject* renderer)
4188 { 4155 {
4189 if (!document().styleEngine()->usesFirstLetterRules()) 4156 ASSERT(renderer->isText());
4190 return; 4157
4191 // Don't recur 4158 RenderText* textRenderer = toRenderText(renderer);
4192 if (style()->styleType() == FIRST_LETTER) 4159 String result = textRenderer->originalText();
4193 return; 4160
4194 4161 // BR and Word breaks are not allowed to be part of the first-letter
4195 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find 4162 // pseudo element, so we don't fallback to text() in those cases.
4196 // an efficient way to check for that situation though before implementing a nything. 4163 if (!result && !textRenderer->isBR() && !textRenderer->isWordBreak())
eseidel 2014/03/27 21:48:11 Are you sure !result does what you want? That's
mario.prada 2014/03/28 01:08:48 My intention here was to check null and, if that's
4197 RenderObject* firstLetterBlock = findFirstLetterBlock(this); 4164 result = textRenderer->text();
4198 if (!firstLetterBlock) 4165
4199 return; 4166 return result;
4200 4167 }
4201 // Drill into inlines looking for our first text child. 4168
4169 static bool shouldConsiderTextForFirstLetter(const String& text, unsigned& lengt h, bool& leadingSpacesChecked, bool& leadingPunctuationChecked, bool& firstLette rRenderersFound)
4170 {
4171 // Early return in case we encounter the wrong characters at the beginning.
4172 if ((leadingSpacesChecked && isSpaceForFirstLetter(text[0]))
eseidel 2014/03/27 21:45:30 What if text is empty?
mario.prada 2014/03/28 01:08:48 I think in that case the flow will continue all th
4173 || (firstLetterRenderersFound && !isPunctuationForFirstLetter(text[0])))
4174 return false;
4175
4176 // Now start looking for valid characters for the firs-letter pseudo element .
eseidel 2014/03/27 21:45:30 "firs-letter"
mario.prada 2014/03/28 01:08:48 English is not my mother tongue, so I won't discus
4177 unsigned textLength = text.length();
4178 length = 0;
4179
4180 if (!leadingSpacesChecked && length < textLength) {
4181 while (length < textLength && isSpaceForFirstLetter(text[length]))
4182 length++;
4183
4184 // We now we finished checking if there are still more characters.
eseidel 2014/03/27 21:45:30 We now we?
mario.prada 2014/03/28 01:08:48 :) Ok. I'll fix this too, no problem
4185 if (length < textLength)
4186 leadingSpacesChecked = true;
4187 }
4188
4189 // If leading spaces have been checked, account now for leading punctuation.
4190 if (!leadingPunctuationChecked && length < textLength) {
4191 while (length < textLength && isPunctuationForFirstLetter(text[length]))
4192 length++;
4193
4194 // We now we finished checking if there are still more characters.
4195 if (length < textLength)
4196 leadingPunctuationChecked = true;
4197 }
4198
4199 // Now account for the next character for first letter and the trailing punc tuation (if any).
4200 if (!firstLetterRenderersFound && length < textLength) {
4201 // Now spaces are allowed between leading punctuation and the letter.
4202 if (isSpaceForFirstLetter(text[length]))
4203 return false;
4204
4205 firstLetterRenderersFound = true;
4206 length++;
4207 }
4208
4209 // Keep looking allowed punctuation for the :first-letter.
4210 for (unsigned scanLength = length; scanLength < textLength; ++scanLength) {
4211 UChar c = text[scanLength];
4212 if (!isPunctuationForFirstLetter(c))
4213 break;
4214
4215 length = scanLength + 1;
4216 }
4217
4218 // Tell the caller that we should keep calling this function for the text of the next renderer.
4219 return true;
4220 }
4221
4222 // This helper function fills the vector of RenderObjects with the ones that wou ld be considered
4223 // as part of the first-letter pseudo element and returns the number of characte rs in the last
4224 // renderer found that should be considered as part of such a pseudo element (th e other renderers
4225 // in the Vector will be considered "in full"). Also, this function might update the firstLetterBlock
4226 // pointer if a lower-level node with first-letter style is found while traversi ng the subtree under
4227 // the RenderObject originally passed through that parameter.
4228 static unsigned findTextRenderersForFirstLetterBlock(RenderObject*& firstLetterB lock, Vector<RenderObject*>& renderers)
4229 {
4230 unsigned length = 0;
4231 unsigned previousLength = 0;
4232 bool leadingSpacesChecked = false;
4233 bool leadingPunctuationChecked = false;
4234 bool firstLetterRenderersFound = false;
4235
4236 // Drill into inlines looking for the render objects to be transformed into first-letter pseudoelements.
4202 RenderObject* currChild = firstLetterBlock->firstChild(); 4237 RenderObject* currChild = firstLetterBlock->firstChild();
4203 unsigned length = 0;
4204 while (currChild) { 4238 while (currChild) {
4205 if (currChild->isText()) { 4239 if (currChild->isText()) {
4206 // FIXME: If there is leading punctuation in a different RenderText than 4240 previousLength = length;
4207 // the first letter, we'll not apply the correct style to it. 4241
4208 length = firstLetterLength(toRenderText(currChild)->originalText()); 4242 // shouldConsiderTextForFirstLetter() will set the right values for the 'length' variable and those booleans.
4243 String text = rendererTextForFirstLetter(currChild);
4244 if (!shouldConsiderTextForFirstLetter(text, length, leadingSpacesChe cked, leadingPunctuationChecked, firstLetterRenderersFound))
4245 break;
4246
4247 // Save the renderer if anything valis was found, so we can apply th e style to later.
4209 if (length) 4248 if (length)
4249 renderers.append(currChild);
4250
4251 // No need to keep looking if we haven't found anything with the
4252 // current renderer or if we already made a decision.
4253 if (!length || length < text.length())
4210 break; 4254 break;
4211 currChild = currChild->nextSibling(); 4255
4256 // We need to look the next object traversing the tree in preorder a s if the current renderer
4257 // was a leaf node (which probably is anyway) but without leaving th e scope of the parent block.
4258 currChild = currChild->nextInPreOrderAfterChildren(firstLetterBlock) ;
4212 } else if (currChild->isListMarker()) { 4259 } else if (currChild->isListMarker()) {
4213 currChild = currChild->nextSibling(); 4260 currChild = currChild->nextSibling();
4214 } else if (currChild->isFloatingOrOutOfFlowPositioned()) { 4261 } else if (currChild->isFloatingOrOutOfFlowPositioned()) {
4215 if (currChild->style()->styleType() == FIRST_LETTER) { 4262 if (currChild->style()->styleType() == FIRST_LETTER) {
4216 currChild = currChild->firstChild(); 4263 currChild = currChild->firstChild();
4264 if (currChild) {
4265 // If found a floating/out-of-flow element with the first_le tter
4266 // style already applied, it means it has been previously id entified
4267 // and so we should discard whatever we found so far and use that.
4268 firstLetterRenderersFound = true;
4269 renderers.append(currChild);
4270 }
4217 break; 4271 break;
4218 } 4272 }
4219 currChild = currChild->nextSibling(); 4273 currChild = currChild->nextSibling();
4220 } else if (currChild->isReplaced() || currChild->isRenderButton() || cur rChild->isMenuList()) 4274 } else if (currChild->isReplaced() || currChild->isRenderButton() || cur rChild->isMenuList())
4221 break; 4275 break;
4222 else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild-> canHaveGeneratedChildren()) { 4276 else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild-> canHaveGeneratedChildren()) {
4223 // We found a lower-level node with first-letter, which supersedes t he higher-level style 4277 // We found a lower-level node with first-letter, which supersedes t he higher-level style.
4224 firstLetterBlock = currChild; 4278 firstLetterBlock = currChild;
4225 currChild = currChild->firstChild(); 4279 currChild = currChild->firstChild();
4226 } else 4280 } else
4227 currChild = currChild->firstChild(); 4281 currChild = currChild->firstChild();
4228 } 4282 }
4229 4283
4230 if (!currChild) 4284 if (!firstLetterRenderersFound) {
4231 return; 4285 // Empty the list of renderers if we did not find a correct set of them
4232 4286 // to further generate new elements to apply the first-letter style over .
4233 // If the child already has style, then it has already been created, so we j ust want 4287 renderers.clear();
4234 // to update it. 4288 } else if (!length) {
4235 if (currChild->parent()->style()->styleType() == FIRST_LETTER) { 4289 // If we found renderers for the first-letter and length is zero it mean s that the last valid
4236 updateFirstLetterStyle(firstLetterBlock, currChild); 4290 // renderer was added in the previous iteration of the loop, so we consi der its length instead.
4237 return; 4291 length = previousLength;
4238 } 4292 }
4239 4293
4240 // FIXME: This black-list of disallowed RenderText subclasses is fragile. 4294 // Return the number of characters used for the first-letter pseudo element coming
4241 // Should counter be on this list? What about RenderTextFragment? 4295 // from the last text renderer found, needed to create the last generated re nderer.
4242 if (!currChild->isText() || currChild->isBR() || toRenderText(currChild)->is WordBreak()) 4296 return renderers.isEmpty() ? 0 : length;
4243 return; 4297 }
4244 4298
4245 // Our layout state is not valid for the repaints we are going to trigger by 4299 void RenderBlock::updateFirstLetter()
4246 // adding and removing children of firstLetterContainer. 4300 {
4247 LayoutStateDisabler layoutStateDisabler(*this); 4301 if (!document().styleEngine()->usesFirstLetterRules())
4248 4302 return;
4249 createFirstLetterRenderer(firstLetterBlock, currChild, length); 4303
4304 // Don't recur.
eseidel 2014/03/27 21:45:30 recurse?
mario.prada 2014/03/28 01:08:48 That's not from my patch, I've just moved it aroun
4305 if (style()->styleType() == FIRST_LETTER)
4306 return;
4307
4308 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
4309 // an efficient way to check for that situation though before implementing a nything.
4310 RenderObject* firstLetterBlock = findFirstLetterBlock(this);
4311 if (!firstLetterBlock)
4312 return;
4313
4314 // Find the renderers to apply the first-letter style over.
4315 Vector<RenderObject*> renderers;
4316 unsigned lengthForLastRenderer = findTextRenderersForFirstLetterBlock(firstL etterBlock, renderers);
4317 if (renderers.isEmpty())
4318 return;
4319
4320 // Create the new renderers for the first-letter pseudo elements.
4321 for (Vector<RenderObject*>::const_iterator it = renderers.begin(); it != ren derers.end(); ++it) {
4322 RenderObject* currentRenderer = *it;
4323 ASSERT(currentRenderer->isText());
4324
4325 // If the child already has style, then it has already been created, so we just want
4326 // to update it.
4327 if (currentRenderer->parent()->style()->styleType() == FIRST_LETTER) {
4328 updateFirstLetterStyle(firstLetterBlock, currentRenderer);
4329 continue;
4330 }
4331
4332 // FIXME: This black-list of disallowed RenderText subclasses is fragile .
4333 // Should counter be on this list? What about RenderTextFragment?
4334 RenderText* textRenderer = toRenderText(currentRenderer);
4335 if (!currentRenderer->isText() || currentRenderer->isBR() || textRendere r->isWordBreak())
4336 continue;
4337
4338 // Our layout state is not valid for the repaints we are going to trigge r by
4339 // adding and removing children of firstLetterContainer.
4340 LayoutStateDisabler layoutStateDisabler(*this);
4341
4342 unsigned lengthForRenderer = 0;
4343 if (currentRenderer == renderers.last())
4344 lengthForRenderer = lengthForLastRenderer;
4345 else
4346 lengthForRenderer = rendererTextForFirstLetter(currentRenderer).leng th();
4347
4348 if (lengthForRenderer)
4349 createFirstLetterRenderer(firstLetterBlock, currentRenderer, lengthF orRenderer);
4350 }
4250 } 4351 }
4251 4352
4252 // Helper methods for obtaining the last line, computing line counts and heights for line counts 4353 // Helper methods for obtaining the last line, computing line counts and heights for line counts
4253 // (crawling into blocks). 4354 // (crawling into blocks).
4254 static bool shouldCheckLines(RenderObject* obj) 4355 static bool shouldCheckLines(RenderObject* obj)
4255 { 4356 {
4256 return !obj->isFloatingOrOutOfFlowPositioned() 4357 return !obj->isFloatingOrOutOfFlowPositioned()
4257 && obj->isRenderBlock() && obj->style()->height().isAuto() 4358 && obj->isRenderBlock() && obj->style()->height().isAuto()
4258 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERT ICAL); 4359 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERT ICAL);
4259 } 4360 }
(...skipping 765 matching lines...) Expand 10 before | Expand all | Expand 10 after
5025 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* m arkedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const Render Object* obj) const 5126 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* m arkedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const Render Object* obj) const
5026 { 5127 {
5027 showRenderObject(); 5128 showRenderObject();
5028 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRoot Box()) 5129 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRoot Box())
5029 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLa bel2, obj, 1); 5130 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLa bel2, obj, 1);
5030 } 5131 }
5031 5132
5032 #endif 5133 #endif
5033 5134
5034 } // namespace WebCore 5135 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698