Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |