1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "RenderInline.h"
25
26#include "Chrome.h"
27#include "FloatQuad.h"
28#include "FrameSelection.h"
29#include "GraphicsContext.h"
30#include "HitTestResult.h"
31#include "InlineElementBox.h"
32#include "InlineTextBox.h"
33#include "RenderBlock.h"
34#include "RenderChildIterator.h"
35#include "RenderFragmentedFlow.h"
36#include "RenderFullScreen.h"
37#include "RenderGeometryMap.h"
38#include "RenderIterator.h"
39#include "RenderLayer.h"
40#include "RenderLayoutState.h"
41#include "RenderLineBreak.h"
42#include "RenderListMarker.h"
43#include "RenderTable.h"
44#include "RenderTheme.h"
45#include "RenderTreeBuilder.h"
46#include "RenderView.h"
47#include "Settings.h"
48#include "StyleInheritedData.h"
49#include "TransformState.h"
50#include "VisiblePosition.h"
51#include <wtf/IsoMallocInlines.h>
52#include <wtf/SetForScope.h>
53
54#if ENABLE(DASHBOARD_SUPPORT)
55#include "Frame.h"
56#endif
57
58namespace WebCore {
59
60WTF_MAKE_ISO_ALLOCATED_IMPL(RenderInline);
61
62RenderInline::RenderInline(Element& element, RenderStyle&& style)
63 : RenderBoxModelObject(element, WTFMove(style), RenderInlineFlag)
64{
65 setChildrenInline(true);
66}
67
68RenderInline::RenderInline(Document& document, RenderStyle&& style)
69 : RenderBoxModelObject(document, WTFMove(style), RenderInlineFlag)
70{
71 setChildrenInline(true);
72}
73
74void RenderInline::willBeDestroyed()
75{
76#if !ASSERT_DISABLED
77 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
78 if (parent() && style().visibility() == Visibility::Visible && hasOutline()) {
79 bool containingBlockPaintsContinuationOutline = continuation() || isContinuation();
80 if (containingBlockPaintsContinuationOutline) {
81 if (RenderBlock* cb = containingBlock()) {
82 if (RenderBlock* cbCb = cb->containingBlock())
83 ASSERT(!cbCb->paintsContinuationOutline(this));
84 }
85 }
86 }
87#endif
88
89 if (!renderTreeBeingDestroyed()) {
90 if (firstLineBox()) {
91 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
92 // because by then we will have nuked the line boxes.
93 if (isSelectionBorder())
94 frame().selection().setNeedsSelectionUpdate();
95
96 // If line boxes are contained inside a root, that means we're an inline.
97 // In that case, we need to remove all the line boxes so that the parent
98 // lines aren't pointing to deleted children. If the first line box does
99 // not have a parent that means they are either already disconnected or
100 // root lines that can just be destroyed without disconnecting.
101 if (firstLineBox()->parent()) {
102 for (auto* box = firstLineBox(); box; box = box->nextLineBox())
103 box->removeFromParent();
104 }
105 } else if (parent())
106 parent()->dirtyLinesFromChangedChild(*this);
107 }
108
109 m_lineBoxes.deleteLineBoxes();
110
111 RenderBoxModelObject::willBeDestroyed();
112}
113
114void RenderInline::updateFromStyle()
115{
116 RenderBoxModelObject::updateFromStyle();
117
118 // FIXME: Support transforms and reflections on inline flows someday.
119 setHasTransformRelatedProperty(false);
120 setHasReflection(false);
121}
122
123static RenderElement* inFlowPositionedInlineAncestor(RenderElement* p)
124{
125 while (p && p->isRenderInline()) {
126 if (p->isInFlowPositioned())
127 return p;
128 p = p->parent();
129 }
130 return nullptr;
131}
132
133static void updateStyleOfAnonymousBlockContinuations(const RenderBlock& block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
134{
135 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
136 for (RenderBox* box = block.nextSiblingBox(); box && box->isAnonymousBlock(); box = box->nextSiblingBox()) {
137 if (box->style().position() == newStyle->position())
138 continue;
139
140 if (!is<RenderBlock>(*box))
141 continue;
142
143 RenderBlock& block = downcast<RenderBlock>(*box);
144 if (!block.isContinuation())
145 continue;
146
147 // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then
148 // their containing anonymous block should keep its in-flow positioning.
149 RenderInline* continuation = block.inlineContinuation();
150 if (oldStyle->hasInFlowPosition() && inFlowPositionedInlineAncestor(continuation))
151 continue;
152 auto blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block.style(), DisplayType::Block);
153 blockStyle.setPosition(newStyle->position());
154 block.setStyle(WTFMove(blockStyle));
155 }
156}
157
158void RenderInline::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
159{
160 RenderBoxModelObject::styleWillChange(diff, newStyle);
161 // RenderInlines forward their absolute positioned descendants to their (non-anonymous) containing block.
162 // Check if this non-anonymous containing block can hold the absolute positioned elements when the inline is no longer positioned.
163 if (canContainAbsolutelyPositionedObjects() && newStyle.position() == PositionType::Static) {
164 auto* container = containingBlockForAbsolutePosition();
165 if (container && !container->canContainAbsolutelyPositionedObjects())
166 container->removePositionedObjects(nullptr, NewContainingBlock);
167 }
168}
169
170void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
171{
172 RenderBoxModelObject::styleDidChange(diff, oldStyle);
173
174 // Ensure that all of the split inlines pick up the new style. We
175 // only do this if we're an inline, since we don't want to propagate
176 // a block's style to the other inlines.
177 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
178 // and after the block share the same style, but the block doesn't
179 // need to pass its style on to anyone else.
180 auto& newStyle = style();
181 RenderInline* continuation = inlineContinuation();
182 if (continuation && !isContinuation()) {
183 for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineContinuation())
184 currCont->setStyle(RenderStyle::clone(newStyle));
185 // If an inline's in-flow positioning has changed and it is part of an active continuation as a descendant of an anonymous containing block,
186 // then any descendant blocks will need to change their in-flow positioning accordingly.
187 // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
188 if (containingBlock()->isAnonymousBlock() && oldStyle && newStyle.position() != oldStyle->position() && (newStyle.hasInFlowPosition() || oldStyle->hasInFlowPosition()))
189 updateStyleOfAnonymousBlockContinuations(*containingBlock(), &newStyle, oldStyle);
190 }
191
192 if (!alwaysCreateLineBoxes()) {
193 bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasVisibleBoxDecorations() || newStyle.hasBorder() || newStyle.hasPadding() || newStyle.hasMargin() || hasOutline();
194 if (oldStyle && alwaysCreateLineBoxes) {
195 dirtyLineBoxes(false);
196 setNeedsLayout();
197 }
198 setRenderInlineAlwaysCreatesLineBoxes(alwaysCreateLineBoxes);
199 }
200}
201
202void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
203{
204 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
205 // background color will only cause a layout on the first rollover.
206 if (alwaysCreateLineBoxes())
207 return;
208
209 auto* parentStyle = &parent()->style();
210 RenderInline* parentRenderInline = is<RenderInline>(*parent()) ? downcast<RenderInline>(parent()) : nullptr;
211 bool checkFonts = document().inNoQuirksMode();
212 bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
213 || (parentRenderInline && parentStyle->verticalAlign() != VerticalAlign::Baseline)
214 || style().verticalAlign() != VerticalAlign::Baseline
215 || style().textEmphasisMark() != TextEmphasisMark::None
216 || (checkFonts && (!parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(style().fontCascade().fontMetrics())
217 || parentStyle->lineHeight() != style().lineHeight()));
218
219 if (!alwaysCreateLineBoxes && checkFonts && view().usesFirstLineRules()) {
220 // Have to check the first line style as well.
221 parentStyle = &parent()->firstLineStyle();
222 auto& childStyle = firstLineStyle();
223 alwaysCreateLineBoxes = !parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().fontMetrics())
224 || childStyle.verticalAlign() != VerticalAlign::Baseline
225 || parentStyle->lineHeight() != childStyle.lineHeight();
226 }
227
228 if (alwaysCreateLineBoxes) {
229 if (!fullLayout)
230 dirtyLineBoxes(false);
231 setAlwaysCreateLineBoxes();
232 }
233}
234
235LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, unsigned, LayoutUnit* extraWidthToEndOfLine)
236{
237 if (firstChild()) {
238 // This condition is possible if the RenderInline is at an editing boundary,
239 // i.e. the VisiblePosition is:
240 // <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
241 // FIXME: need to figure out how to make this return a valid rect, note that
242 // there are no line boxes created in the above case.
243 return LayoutRect();
244 }
245
246 ASSERT_UNUSED(inlineBox, !inlineBox);
247
248 if (extraWidthToEndOfLine)
249 *extraWidthToEndOfLine = 0;
250
251 LayoutRect caretRect = localCaretRectForEmptyElement(horizontalBorderAndPaddingExtent(), 0);
252
253 if (InlineBox* firstBox = firstLineBox())
254 caretRect.moveBy(LayoutPoint(firstBox->topLeft()));
255
256 return caretRect;
257}
258
259void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
260{
261 m_lineBoxes.paint(this, paintInfo, paintOffset);
262}
263
264template<typename GeneratorContext>
265void RenderInline::generateLineBoxRects(GeneratorContext& context) const
266{
267 if (!alwaysCreateLineBoxes())
268 generateCulledLineBoxRects(context, this);
269 else if (InlineFlowBox* curr = firstLineBox()) {
270 for (; curr; curr = curr->nextLineBox())
271 context.addRect(FloatRect(curr->topLeft(), curr->size()));
272 } else
273 context.addRect(FloatRect());
274}
275
276template<typename GeneratorContext>
277void RenderInline::generateCulledLineBoxRects(GeneratorContext& context, const RenderInline* container) const
278{
279 if (!culledInlineFirstLineBox()) {
280 context.addRect(FloatRect());
281 return;
282 }
283
284 bool isHorizontal = style().isHorizontalWritingMode();
285
286 for (auto& current : childrenOfType<RenderObject>(*this)) {
287 if (current.isFloatingOrOutOfFlowPositioned())
288 continue;
289
290 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
291 // direction (aligned to the root box's baseline).
292 if (is<RenderBox>(current)) {
293 auto& renderBox = downcast<RenderBox>(current);
294 if (renderBox.inlineBoxWrapper()) {
295 const RootInlineBox& rootBox = renderBox.inlineBoxWrapper()->root();
296 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
297 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
298 int logicalHeight = containerStyle.fontCascade().fontMetrics().height();
299 if (isHorizontal)
300 context.addRect(FloatRect(renderBox.inlineBoxWrapper()->x() - renderBox.marginLeft(), logicalTop, renderBox.width() + renderBox.horizontalMarginExtent(), logicalHeight));
301 else
302 context.addRect(FloatRect(logicalTop, renderBox.inlineBoxWrapper()->y() - renderBox.marginTop(), logicalHeight, renderBox.height() + renderBox.verticalMarginExtent()));
303 }
304 } else if (is<RenderInline>(current)) {
305 // If the child doesn't need line boxes either, then we can recur.
306 auto& renderInline = downcast<RenderInline>(current);
307 if (!renderInline.alwaysCreateLineBoxes())
308 renderInline.generateCulledLineBoxRects(context, container);
309 else {
310 for (InlineFlowBox* childLine = renderInline.firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
311 const RootInlineBox& rootBox = childLine->root();
312 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
313 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
314 int logicalHeight = containerStyle.fontMetrics().height();
315 if (isHorizontal) {
316 context.addRect(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
317 logicalTop,
318 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
319 logicalHeight));
320 } else {
321 context.addRect(FloatRect(logicalTop,
322 childLine->y() - childLine->marginLogicalLeft(),
323 logicalHeight,
324 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
325 }
326 }
327 }
328 } else if (is<RenderText>(current)) {
329 auto& currText = downcast<RenderText>(current);
330 for (InlineTextBox* childText = currText.firstTextBox(); childText; childText = childText->nextTextBox()) {
331 const RootInlineBox& rootBox = childText->root();
332 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
333 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
334 int logicalHeight = containerStyle.fontCascade().fontMetrics().height();
335 if (isHorizontal)
336 context.addRect(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
337 else
338 context.addRect(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
339 }
340 } else if (is<RenderLineBreak>(current)) {
341 if (auto* inlineBox = downcast<RenderLineBreak>(current).inlineBoxWrapper()) {
342 // FIXME: This could use a helper to share these with text path.
343 const RootInlineBox& rootBox = inlineBox->root();
344 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
345 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
346 int logicalHeight = containerStyle.fontMetrics().height();
347 if (isHorizontal)
348 context.addRect(FloatRect(inlineBox->x(), logicalTop, inlineBox->logicalWidth(), logicalHeight));
349 else
350 context.addRect(FloatRect(logicalTop, inlineBox->y(), logicalHeight, inlineBox->logicalWidth()));
351 }
352 }
353 }
354}
355
356namespace {
357
358class AbsoluteRectsGeneratorContext {
359public:
360 AbsoluteRectsGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset)
361 : m_rects(rects)
362 , m_accumulatedOffset(accumulatedOffset) { }
363
364 void addRect(const FloatRect& rect)
365 {
366 LayoutRect adjustedRect = LayoutRect(rect);
367 adjustedRect.moveBy(m_accumulatedOffset);
368 m_rects.append(adjustedRect);
369 }
370private:
371 Vector<LayoutRect>& m_rects;
372 const LayoutPoint& m_accumulatedOffset;
373};
374
375} // unnamed namespace
376
377void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
378{
379 Vector<LayoutRect> lineboxRects;
380 AbsoluteRectsGeneratorContext context(lineboxRects, accumulatedOffset);
381 generateLineBoxRects(context);
382 for (const auto& rect : lineboxRects)
383 rects.append(snappedIntRect(rect));
384
385 if (RenderBoxModelObject* continuation = this->continuation()) {
386 if (is<RenderBox>(*continuation)) {
387 auto& box = downcast<RenderBox>(*continuation);
388 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box.locationOffset()));
389 } else
390 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
391 }
392}
393
394
395namespace {
396
397class AbsoluteQuadsGeneratorContext {
398public:
399 AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
400 : m_quads(quads)
401 , m_geometryMap()
402 {
403 m_geometryMap.pushMappingsToAncestor(renderer, nullptr);
404 }
405
406 void addRect(const FloatRect& rect)
407 {
408 m_quads.append(m_geometryMap.absoluteRect(rect));
409 }
410private:
411 Vector<FloatQuad>& m_quads;
412 RenderGeometryMap m_geometryMap;
413};
414
415} // unnamed namespace
416
417void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
418{
419 AbsoluteQuadsGeneratorContext context(this, quads);
420 generateLineBoxRects(context);
421
422 if (RenderBoxModelObject* continuation = this->continuation())
423 continuation->absoluteQuads(quads, wasFixed);
424}
425
426#if PLATFORM(IOS_FAMILY)
427void RenderInline::absoluteQuadsForSelection(Vector<FloatQuad>& quads) const
428{
429 AbsoluteQuadsGeneratorContext context(this, quads);
430 generateLineBoxRects(context);
431}
432#endif
433
434LayoutUnit RenderInline::offsetLeft() const
435{
436 LayoutPoint topLeft;
437 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
438 topLeft = flooredLayoutPoint(firstBox->topLeft());
439 return adjustedPositionRelativeToOffsetParent(topLeft).x();
440}
441
442LayoutUnit RenderInline::offsetTop() const
443{
444 LayoutPoint topLeft;
445 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
446 topLeft = flooredLayoutPoint(firstBox->topLeft());
447 return adjustedPositionRelativeToOffsetParent(topLeft).y();
448}
449
450static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
451{
452 if (margin.isAuto())
453 return 0;
454 if (margin.isFixed())
455 return margin.value();
456 if (margin.isPercentOrCalculated())
457 return minimumValueForLength(margin, std::max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
458 return 0;
459}
460
461LayoutUnit RenderInline::marginLeft() const
462{
463 return computeMargin(this, style().marginLeft());
464}
465
466LayoutUnit RenderInline::marginRight() const
467{
468 return computeMargin(this, style().marginRight());
469}
470
471LayoutUnit RenderInline::marginTop() const
472{
473 return computeMargin(this, style().marginTop());
474}
475
476LayoutUnit RenderInline::marginBottom() const
477{
478 return computeMargin(this, style().marginBottom());
479}
480
481LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
482{
483 return computeMargin(this, style().marginStartUsing(otherStyle ? otherStyle : &style()));
484}
485
486LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
487{
488 return computeMargin(this, style().marginEndUsing(otherStyle ? otherStyle : &style()));
489}
490
491LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
492{
493 return computeMargin(this, style().marginBeforeUsing(otherStyle ? otherStyle : &style()));
494}
495
496LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
497{
498 return computeMargin(this, style().marginAfterUsing(otherStyle ? otherStyle : &style()));
499}
500
501const char* RenderInline::renderName() const
502{
503 if (isRelativelyPositioned())
504 return "RenderInline (relative positioned)";
505 if (isStickilyPositioned())
506 return "RenderInline (sticky positioned)";
507 // FIXME: Temporary hack while the new generated content system is being implemented.
508 if (isPseudoElement())
509 return "RenderInline (generated)";
510 if (isAnonymous())
511 return "RenderInline (generated)";
512 return "RenderInline";
513}
514
515bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
516 const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
517{
518 return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction);
519}
520
521namespace {
522
523class HitTestCulledInlinesGeneratorContext {
524public:
525 HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location)
526 : m_intersected(false)
527 , m_region(region)
528 , m_location(location)
529 { }
530
531 void addRect(const FloatRect& rect)
532 {
533 m_intersected = m_intersected || m_location.intersects(rect);
534 m_region.unite(enclosingIntRect(rect));
535 }
536
537 bool intersected() const { return m_intersected; }
538
539private:
540 bool m_intersected;
541 Region& m_region;
542 const HitTestLocation& m_location;
543};
544
545} // unnamed namespace
546
547bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
548{
549 ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes());
550 if (!visibleToHitTesting())
551 return false;
552
553 HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset));
554
555 Region regionResult;
556 HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation);
557 generateCulledLineBoxRects(context, this);
558
559 if (context.intersected()) {
560 updateHitTestResult(result, tmpLocation.point());
561 // We cannot use addNodeToListBasedTestResult to determine if we fully enclose the hit-test area
562 // because it can only handle rectangular targets.
563 result.addNodeToListBasedTestResult(element(), request, locationInContainer);
564 return regionResult.contains(tmpLocation.boundingBox());
565 }
566 return false;
567}
568
569VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point, const RenderFragmentContainer* fragment)
570{
571 // FIXME: Does not deal with relative or sticky positioned inlines (should it?)
572 RenderBlock& containingBlock = *this->containingBlock();
573 if (firstLineBox()) {
574 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
575 // should try to find a result by asking our containing block.
576 return containingBlock.positionForPoint(point, fragment);
577 }
578
579 // Translate the coords from the pre-anonymous block to the post-anonymous block.
580 LayoutPoint parentBlockPoint = containingBlock.location() + point;
581 RenderBoxModelObject* continuation = this->continuation();
582 while (continuation) {
583 RenderBlock* currentBlock = continuation->isInline() ? continuation->containingBlock() : downcast<RenderBlock>(continuation);
584 if (continuation->isInline() || continuation->firstChild())
585 return continuation->positionForPoint(parentBlockPoint - currentBlock->locationOffset(), fragment);
586 continuation = continuation->inlineContinuation();
587 }
588
589 return RenderBoxModelObject::positionForPoint(point, fragment);
590}
591
592namespace {
593
594class LinesBoundingBoxGeneratorContext {
595public:
596 LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
597
598 void addRect(const FloatRect& rect)
599 {
600 m_rect.uniteIfNonZero(rect);
601 }
602private:
603 FloatRect& m_rect;
604};
605
606} // unnamed namespace
607
608IntRect RenderInline::linesBoundingBox() const
609{
610 if (!alwaysCreateLineBoxes()) {
611 ASSERT(!firstLineBox());
612 FloatRect floatResult;
613 LinesBoundingBoxGeneratorContext context(floatResult);
614 generateCulledLineBoxRects(context, this);
615 return enclosingIntRect(floatResult);
616 }
617
618 IntRect result;
619
620 // See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been
621 // unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug
622 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
623 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
624 if (firstLineBox() && lastLineBox()) {
625 // Return the width of the minimal left side and the maximal right side.
626 float logicalLeftSide = 0;
627 float logicalRightSide = 0;
628 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
629 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
630 logicalLeftSide = curr->logicalLeft();
631 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
632 logicalRightSide = curr->logicalRight();
633 }
634
635 bool isHorizontal = style().isHorizontalWritingMode();
636
637 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
638 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
639 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
640 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
641 result = enclosingIntRect(FloatRect(x, y, width, height));
642 }
643
644 return result;
645}
646
647InlineBox* RenderInline::culledInlineFirstLineBox() const
648{
649 for (auto& current : childrenOfType<RenderObject>(*this)) {
650 if (current.isFloatingOrOutOfFlowPositioned())
651 continue;
652
653 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
654 // direction (aligned to the root box's baseline).
655 if (is<RenderBox>(current)) {
656 auto& renderBox = downcast<RenderBox>(current);
657 if (renderBox.inlineBoxWrapper())
658 return renderBox.inlineBoxWrapper();
659 } else if (is<RenderLineBreak>(current)) {
660 auto& renderBR = downcast<RenderLineBreak>(current);
661 if (renderBR.inlineBoxWrapper())
662 return renderBR.inlineBoxWrapper();
663 } else if (is<RenderInline>(current)) {
664 auto& renderInline = downcast<RenderInline>(current);
665 if (InlineBox* result = renderInline.firstLineBoxIncludingCulling())
666 return result;
667 } else if (is<RenderText>(current)) {
668 auto& renderText = downcast<RenderText>(current);
669 if (renderText.firstTextBox())
670 return renderText.firstTextBox();
671 }
672 }
673 return nullptr;
674}
675
676InlineBox* RenderInline::culledInlineLastLineBox() const
677{
678 for (RenderObject* current = lastChild(); current; current = current->previousSibling()) {
679 if (current->isFloatingOrOutOfFlowPositioned())
680 continue;
681
682 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
683 // direction (aligned to the root box's baseline).
684 if (is<RenderBox>(*current)) {
685 const auto& renderBox = downcast<RenderBox>(*current);
686 if (renderBox.inlineBoxWrapper())
687 return renderBox.inlineBoxWrapper();
688 } else if (is<RenderLineBreak>(*current)) {
689 RenderLineBreak& renderBR = downcast<RenderLineBreak>(*current);
690 if (renderBR.inlineBoxWrapper())
691 return renderBR.inlineBoxWrapper();
692 } else if (is<RenderInline>(*current)) {
693 RenderInline& renderInline = downcast<RenderInline>(*current);
694 if (InlineBox* result = renderInline.lastLineBoxIncludingCulling())
695 return result;
696 } else if (is<RenderText>(*current)) {
697 RenderText& renderText = downcast<RenderText>(*current);
698 if (renderText.lastTextBox())
699 return renderText.lastTextBox();
700 }
701 }
702 return nullptr;
703}
704
705LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
706{
707 FloatRect floatResult;
708 LinesBoundingBoxGeneratorContext context(floatResult);
709 generateCulledLineBoxRects(context, this);
710 LayoutRect result(enclosingLayoutRect(floatResult));
711 bool isHorizontal = style().isHorizontalWritingMode();
712 for (auto& current : childrenOfType<RenderObject>(*this)) {
713 if (current.isFloatingOrOutOfFlowPositioned())
714 continue;
715
716 // For overflow we just have to propagate by hand and recompute it all.
717 if (is<RenderBox>(current)) {
718 auto& renderBox = downcast<RenderBox>(current);
719 if (!renderBox.hasSelfPaintingLayer() && renderBox.inlineBoxWrapper()) {
720 LayoutRect logicalRect = renderBox.logicalVisualOverflowRectForPropagation(&style());
721 if (isHorizontal) {
722 logicalRect.moveBy(renderBox.location());
723 result.uniteIfNonZero(logicalRect);
724 } else {
725 logicalRect.moveBy(renderBox.location());
726 result.uniteIfNonZero(logicalRect.transposedRect());
727 }
728 }
729 } else if (is<RenderInline>(current)) {
730 // If the child doesn't need line boxes either, then we can recur.
731 auto& renderInline = downcast<RenderInline>(current);
732 if (!renderInline.alwaysCreateLineBoxes())
733 result.uniteIfNonZero(renderInline.culledInlineVisualOverflowBoundingBox());
734 else if (!renderInline.hasSelfPaintingLayer())
735 result.uniteIfNonZero(renderInline.linesVisualOverflowBoundingBox());
736 } else if (is<RenderText>(current)) {
737 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
738 // InlineTextBoxes.
739 auto& renderText = downcast<RenderText>(current);
740 result.uniteIfNonZero(renderText.linesVisualOverflowBoundingBox());
741 }
742 }
743 return result;
744}
745
746LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
747{
748 if (!alwaysCreateLineBoxes())
749 return culledInlineVisualOverflowBoundingBox();
750
751 if (!firstLineBox() || !lastLineBox())
752 return LayoutRect();
753
754 // Return the width of the minimal left side and the maximal right side.
755 LayoutUnit logicalLeftSide = LayoutUnit::max();
756 LayoutUnit logicalRightSide = LayoutUnit::min();
757 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
758 logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
759 logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
760 }
761
762 const RootInlineBox& firstRootBox = firstLineBox()->root();
763 const RootInlineBox& lastRootBox = lastLineBox()->root();
764
765 LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
766 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
767 LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop;
768
769 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
770 if (!style().isHorizontalWritingMode())
771 rect = rect.transposedRect();
772 return rect;
773}
774
775LayoutRect RenderInline::linesVisualOverflowBoundingBoxInFragment(const RenderFragmentContainer* fragment) const
776{
777 ASSERT(alwaysCreateLineBoxes());
778 ASSERT(fragment);
779
780 if (!firstLineBox() || !lastLineBox())
781 return LayoutRect();
782
783 // Return the width of the minimal left side and the maximal right side.
784 LayoutUnit logicalLeftSide = LayoutUnit::max();
785 LayoutUnit logicalRightSide = LayoutUnit::min();
786 LayoutUnit logicalTop;
787 LayoutUnit logicalHeight;
788 InlineFlowBox* lastInlineInFragment = 0;
789 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
790 const RootInlineBox& root = curr->root();
791 if (root.containingFragment() != fragment) {
792 if (lastInlineInFragment)
793 break;
794 continue;
795 }
796
797 if (!lastInlineInFragment)
798 logicalTop = curr->logicalTopVisualOverflow(root.lineTop());
799
800 lastInlineInFragment = curr;
801
802 logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
803 logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
804 }
805
806 if (!lastInlineInFragment)
807 return LayoutRect();
808
809 logicalHeight = lastInlineInFragment->logicalBottomVisualOverflow(lastInlineInFragment->root().lineBottom()) - logicalTop;
810
811 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
812
813 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
814 if (!style().isHorizontalWritingMode())
815 rect = rect.transposedRect();
816 return rect;
817}
818
819LayoutRect RenderInline::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
820{
821 // Only first-letter renderers are allowed in here during layout. They mutate the tree triggering repaints.
822 ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled() || style().styleType() == PseudoId::FirstLetter || hasSelfPaintingLayer());
823
824 if (!firstLineBoxIncludingCulling() && !continuation())
825 return LayoutRect();
826
827 LayoutRect repaintRect(linesVisualOverflowBoundingBox());
828 bool hitRepaintContainer = false;
829
830 // We need to add in the in-flow position offsets of any inlines (including us) up to our
831 // containing block.
832 RenderBlock* containingBlock = this->containingBlock();
833 for (const RenderElement* inlineFlow = this; is<RenderInline>(inlineFlow) && inlineFlow != containingBlock;
834 inlineFlow = inlineFlow->parent()) {
835 if (inlineFlow == repaintContainer) {
836 hitRepaintContainer = true;
837 break;
838 }
839 if (inlineFlow->style().hasInFlowPosition() && inlineFlow->hasLayer())
840 repaintRect.move(downcast<RenderInline>(*inlineFlow).layer()->offsetForInFlowPosition());
841 }
842
843 LayoutUnit outlineSize = style().outlineSize();
844 repaintRect.inflate(outlineSize);
845
846 if (hitRepaintContainer || !containingBlock)
847 return repaintRect;
848
849 if (containingBlock->hasOverflowClip())
850 containingBlock->applyCachedClipAndScrollPosition(repaintRect, repaintContainer, visibleRectContextForRepaint());
851
852 repaintRect = containingBlock->computeRectForRepaint(repaintRect, repaintContainer);
853
854 if (outlineSize) {
855 for (auto& child : childrenOfType<RenderElement>(*this))
856 repaintRect.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineSize));
857
858 if (RenderBoxModelObject* continuation = this->continuation()) {
859 if (!continuation->isInline() && continuation->parent())
860 repaintRect.unite(continuation->rectWithOutlineForRepaint(repaintContainer, outlineSize));
861 }
862 }
863
864 return repaintRect;
865}
866
867LayoutRect RenderInline::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
868{
869 LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
870 for (auto& child : childrenOfType<RenderElement>(*this))
871 r.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineWidth));
872 return r;
873}
874
875LayoutRect RenderInline::computeVisibleRectUsingPaintOffset(const LayoutRect& rect) const
876{
877 LayoutRect adjustedRect = rect;
878 auto* layoutState = view().frameView().layoutContext().layoutState();
879 if (style().hasInFlowPosition() && layer())
880 adjustedRect.move(layer()->offsetForInFlowPosition());
881 adjustedRect.move(layoutState->paintOffset());
882 if (layoutState->isClipped())
883 adjustedRect.intersect(layoutState->clipRect());
884 return adjustedRect;
885}
886
887Optional<LayoutRect> RenderInline::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
888{
889 // Repaint offset cache is only valid for root-relative repainting
890 if (view().frameView().layoutContext().isPaintOffsetCacheEnabled() && !container && !context.m_options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
891 return computeVisibleRectUsingPaintOffset(rect);
892
893 if (container == this)
894 return rect;
895
896 bool containerSkipped;
897 RenderElement* localContainer = this->container(container, containerSkipped);
898 if (!localContainer)
899 return rect;
900
901 LayoutRect adjustedRect = rect;
902 LayoutPoint topLeft = adjustedRect.location();
903
904 if (style().hasInFlowPosition() && layer()) {
905 // Apply the in-flow position offset when invalidating a rectangle. The layer
906 // is translated, but the render box isn't, so we need to do this to get the
907 // right dirty rect. Since this is called from RenderObject::setStyle, the relative or sticky position
908 // flag on the RenderObject has been cleared, so use the one on the style().
909 topLeft += layer()->offsetForInFlowPosition();
910 }
911
912 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
913 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
914 adjustedRect.setLocation(topLeft);
915 if (localContainer->hasOverflowClip()) {
916 // FIXME: Respect the value of context.m_options.
917 SetForScope<OptionSet<VisibleRectContextOption>> change(context.m_options, context.m_options | VisibleRectContextOption::ApplyCompositedContainerScrolls);
918 bool isEmpty = !downcast<RenderBox>(*localContainer).applyCachedClipAndScrollPosition(adjustedRect, container, context);
919 if (isEmpty) {
920 if (context.m_options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
921 return WTF::nullopt;
922 return adjustedRect;
923 }
924 }
925
926 if (containerSkipped) {
927 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
928 LayoutSize containerOffset = container->offsetFromAncestorContainer(*localContainer);
929 adjustedRect.move(-containerOffset);
930 return adjustedRect;
931 }
932 return localContainer->computeVisibleRectInContainer(adjustedRect, container, context);
933}
934
935LayoutSize RenderInline::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const
936{
937 ASSERT(&container == this->container());
938
939 LayoutSize offset;
940 if (isInFlowPositioned())
941 offset += offsetForInFlowPosition();
942
943 if (is<RenderBox>(container))
944 offset -= toLayoutSize(downcast<RenderBox>(container).scrollPosition());
945
946 if (offsetDependsOnPoint)
947 *offsetDependsOnPoint = (is<RenderBox>(container) && container.style().isFlippedBlocksWritingMode()) || is<RenderFragmentedFlow>(container);
948
949 return offset;
950}
951
952void RenderInline::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
953{
954 if (repaintContainer == this)
955 return;
956
957 if (view().frameView().layoutContext().isPaintOffsetCacheEnabled() && !repaintContainer) {
958 auto* layoutState = view().frameView().layoutContext().layoutState();
959 LayoutSize offset = layoutState->paintOffset();
960 if (style().hasInFlowPosition() && layer())
961 offset += layer()->offsetForInFlowPosition();
962 transformState.move(offset);
963 return;
964 }
965
966 bool containerSkipped;
967 RenderElement* container = this->container(repaintContainer, containerSkipped);
968 if (!container)
969 return;
970
971 if (mode & ApplyContainerFlip && is<RenderBox>(*container)) {
972 if (container->style().isFlippedBlocksWritingMode()) {
973 LayoutPoint centerPoint(transformState.mappedPoint());
974 transformState.move(downcast<RenderBox>(*container).flipForWritingMode(centerPoint) - centerPoint);
975 }
976 mode &= ~ApplyContainerFlip;
977 }
978
979 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint()));
980
981 bool preserve3D = mode & UseTransforms && (container->style().preserves3D() || style().preserves3D());
982 if (mode & UseTransforms && shouldUseTransformFromContainer(container)) {
983 TransformationMatrix t;
984 getTransformFromContainer(container, containerOffset, t);
985 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
986 } else
987 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
988
989 if (containerSkipped) {
990 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
991 // to just subtract the delta between the repaintContainer and o.
992 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(*container);
993 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
994 return;
995 }
996
997 container->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
998}
999
1000const RenderObject* RenderInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
1001{
1002 ASSERT(ancestorToStopAt != this);
1003
1004 bool ancestorSkipped;
1005 RenderElement* container = this->container(ancestorToStopAt, ancestorSkipped);
1006 if (!container)
1007 return nullptr;
1008
1009 LayoutSize adjustmentForSkippedAncestor;
1010 if (ancestorSkipped) {
1011 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1012 // to just subtract the delta between the ancestor and o.
1013 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(*container);
1014 }
1015
1016 bool offsetDependsOnPoint = false;
1017 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(), &offsetDependsOnPoint);
1018
1019 bool preserve3D = container->style().preserves3D() || style().preserves3D();
1020 if (shouldUseTransformFromContainer(container)) {
1021 TransformationMatrix t;
1022 getTransformFromContainer(container, containerOffset, t);
1023 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); // FIXME: right?
1024 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint);
1025 } else {
1026 containerOffset += adjustmentForSkippedAncestor;
1027 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint);
1028 }
1029
1030 return ancestorSkipped ? ancestorToStopAt : container;
1031}
1032
1033void RenderInline::updateDragState(bool dragOn)
1034{
1035 RenderBoxModelObject::updateDragState(dragOn);
1036 if (RenderBoxModelObject* continuation = this->continuation())
1037 continuation->updateDragState(dragOn);
1038}
1039
1040void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1041{
1042 if (result.innerNode())
1043 return;
1044
1045 LayoutPoint localPoint(point);
1046 if (Element* element = this->element()) {
1047 if (isContinuation()) {
1048 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1049 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1050 RenderBlock* firstBlock = element->renderer()->containingBlock();
1051
1052 // Get our containing block.
1053 RenderBox* block = containingBlock();
1054 localPoint.moveBy(block->location() - firstBlock->locationOffset());
1055 }
1056
1057 result.setInnerNode(element);
1058 if (!result.innerNonSharedNode())
1059 result.setInnerNonSharedNode(element);
1060 result.setLocalPoint(localPoint);
1061 }
1062}
1063
1064void RenderInline::dirtyLineBoxes(bool fullLayout)
1065{
1066 if (fullLayout) {
1067 m_lineBoxes.deleteLineBoxes();
1068 return;
1069 }
1070
1071 if (!alwaysCreateLineBoxes()) {
1072 // We have to grovel into our children in order to dirty the appropriate lines.
1073 for (auto& current : childrenOfType<RenderObject>(*this)) {
1074 if (current.isFloatingOrOutOfFlowPositioned())
1075 continue;
1076 if (is<RenderBox>(current) && !current.needsLayout()) {
1077 auto& renderBox = downcast<RenderBox>(current);
1078 if (renderBox.inlineBoxWrapper())
1079 renderBox.inlineBoxWrapper()->root().markDirty();
1080 } else if (!current.selfNeedsLayout()) {
1081 if (is<RenderInline>(current)) {
1082 auto& renderInline = downcast<RenderInline>(current);
1083 for (InlineFlowBox* childLine = renderInline.firstLineBox(); childLine; childLine = childLine->nextLineBox())
1084 childLine->root().markDirty();
1085 } else if (is<RenderText>(current)) {
1086 auto& renderText = downcast<RenderText>(current);
1087 for (InlineTextBox* childText = renderText.firstTextBox(); childText; childText = childText->nextTextBox())
1088 childText->root().markDirty();
1089 } else if (is<RenderLineBreak>(current)) {
1090 auto& renderBR = downcast<RenderLineBreak>(current);
1091 if (renderBR.inlineBoxWrapper())
1092 renderBR.inlineBoxWrapper()->root().markDirty();
1093 }
1094 }
1095 }
1096 } else
1097 m_lineBoxes.dirtyLineBoxes();
1098}
1099
1100void RenderInline::deleteLines()
1101{
1102 m_lineBoxes.deleteLineBoxTree();
1103}
1104
1105std::unique_ptr<InlineFlowBox> RenderInline::createInlineFlowBox()
1106{
1107 return std::make_unique<InlineFlowBox>(*this);
1108}
1109
1110InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1111{
1112 setAlwaysCreateLineBoxes();
1113 auto newFlowBox = createInlineFlowBox();
1114 auto flowBox = newFlowBox.get();
1115 m_lineBoxes.appendLineBox(WTFMove(newFlowBox));
1116 return flowBox;
1117}
1118
1119LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1120{
1121 if (firstLine && view().usesFirstLineRules()) {
1122 const RenderStyle& firstLineStyle = this->firstLineStyle();
1123 if (&firstLineStyle != &style())
1124 return firstLineStyle.computedLineHeight();
1125 }
1126
1127 return style().computedLineHeight();
1128}
1129
1130int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1131{
1132 const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
1133 const FontMetrics& fontMetrics = style.fontMetrics();
1134 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1135}
1136
1137LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox* child) const
1138{
1139 // FIXME: This function isn't right with mixed writing modes.
1140
1141 ASSERT(isInFlowPositioned());
1142 if (!isInFlowPositioned())
1143 return LayoutSize();
1144
1145 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1146 // box from the rest of the content, but only in the cases where we know we're positioned
1147 // relative to the inline itself.
1148
1149 LayoutSize logicalOffset;
1150 LayoutUnit inlinePosition;
1151 LayoutUnit blockPosition;
1152 if (firstLineBox()) {
1153 inlinePosition = LayoutUnit::fromFloatRound(firstLineBox()->logicalLeft());
1154 blockPosition = firstLineBox()->logicalTop();
1155 } else {
1156 inlinePosition = layer()->staticInlinePosition();
1157 blockPosition = layer()->staticBlockPosition();
1158 }
1159
1160 if (!child->style().hasStaticInlinePosition(style().isHorizontalWritingMode()))
1161 logicalOffset.setWidth(inlinePosition);
1162
1163 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
1164 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
1165 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
1166 // do.
1167 else if (!child->style().isOriginalDisplayInlineType())
1168 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
1169 logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
1170
1171 if (!child->style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
1172 logicalOffset.setHeight(blockPosition);
1173
1174 return style().isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1175}
1176
1177void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1178{
1179 if (!parent())
1180 return;
1181
1182 // FIXME: We can do better.
1183 repaint();
1184}
1185
1186void RenderInline::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
1187{
1188 AbsoluteRectsGeneratorContext context(rects, additionalOffset);
1189 generateLineBoxRects(context);
1190
1191 for (auto& child : childrenOfType<RenderElement>(*this)) {
1192 if (is<RenderListMarker>(child))
1193 continue;
1194 FloatPoint pos(additionalOffset);
1195 // FIXME: This doesn't work correctly with transforms.
1196 if (child.hasLayer())
1197 pos = child.localToContainerPoint(FloatPoint(), paintContainer);
1198 else if (is<RenderBox>(child))
1199 pos.move(downcast<RenderBox>(child).locationOffset());
1200 child.addFocusRingRects(rects, flooredIntPoint(pos), paintContainer);
1201 }
1202
1203 if (RenderBoxModelObject* continuation = this->continuation()) {
1204 if (continuation->isInline())
1205 continuation->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + continuation->containingBlock()->location() - containingBlock()->location())), paintContainer);
1206 else
1207 continuation->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + downcast<RenderBox>(*continuation).location() - containingBlock()->location())), paintContainer);
1208 }
1209}
1210
1211void RenderInline::paintOutline(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1212{
1213 if (!hasOutline())
1214 return;
1215
1216 auto& styleToUse = style();
1217 // Only paint the focus ring by hand if the theme isn't able to draw it.
1218 if (styleToUse.outlineStyleIsAuto() == OutlineIsAuto::On && !theme().supportsFocusRing(styleToUse)) {
1219 Vector<LayoutRect> focusRingRects;
1220 addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer);
1221 paintFocusRing(paintInfo, styleToUse, focusRingRects);
1222 }
1223
1224 if (hasOutlineAnnotation() && styleToUse.outlineStyleIsAuto() == OutlineIsAuto::Off && !theme().supportsFocusRing(styleToUse))
1225 addPDFURLRect(paintInfo, paintOffset);
1226
1227 GraphicsContext& graphicsContext = paintInfo.context();
1228 if (graphicsContext.paintingDisabled())
1229 return;
1230
1231 if (styleToUse.outlineStyleIsAuto() == OutlineIsAuto::On || !styleToUse.hasOutline())
1232 return;
1233
1234 Vector<LayoutRect> rects;
1235 rects.append(LayoutRect());
1236 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1237 const RootInlineBox& rootBox = curr->root();
1238 LayoutUnit top = std::max<LayoutUnit>(rootBox.lineTop(), curr->logicalTop());
1239 LayoutUnit bottom = std::min<LayoutUnit>(rootBox.lineBottom(), curr->logicalBottom());
1240 rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1241 }
1242 rects.append(LayoutRect());
1243
1244 Color outlineColor = styleToUse.visitedDependentColorWithColorFilter(CSSPropertyOutlineColor);
1245 bool useTransparencyLayer = !outlineColor.isOpaque();
1246 if (useTransparencyLayer) {
1247 graphicsContext.beginTransparencyLayer(outlineColor.alphaAsFloat());
1248 outlineColor = outlineColor.opaqueColor();
1249 }
1250
1251 for (unsigned i = 1; i < rects.size() - 1; i++)
1252 paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
1253
1254 if (useTransparencyLayer)
1255 graphicsContext.endTransparencyLayer();
1256}
1257
1258void RenderInline::paintOutlineForLine(GraphicsContext& graphicsContext, const LayoutPoint& paintOffset,
1259 const LayoutRect& previousLine, const LayoutRect& thisLine, const LayoutRect& nextLine, const Color& outlineColor)
1260{
1261 const auto& styleToUse = style();
1262 float outlineOffset = styleToUse.outlineOffset();
1263 LayoutRect outlineBoxRect = thisLine;
1264 outlineBoxRect.inflate(outlineOffset);
1265 outlineBoxRect.moveBy(paintOffset);
1266 if (outlineBoxRect.isEmpty())
1267 return;
1268
1269 float outlineWidth = styleToUse.outlineWidth();
1270 BorderStyle outlineStyle = styleToUse.outlineStyle();
1271 bool antialias = shouldAntialiasLines(graphicsContext);
1272
1273 auto adjustedPreviousLine = previousLine;
1274 adjustedPreviousLine.moveBy(paintOffset);
1275 auto adjustedNextLine = nextLine;
1276 adjustedNextLine.moveBy(paintOffset);
1277
1278 float adjacentWidth1 = 0;
1279 float adjacentWidth2 = 0;
1280 // left edge
1281 auto topLeft = outlineBoxRect.minXMinYCorner();
1282 if (previousLine.isEmpty() || thisLine.x() < previousLine.x() || (previousLine.maxX()) <= thisLine.x()) {
1283 topLeft.move(-outlineWidth, -outlineWidth);
1284 adjacentWidth1 = outlineWidth;
1285 } else {
1286 topLeft.move(-outlineWidth, 2 * outlineOffset);
1287 adjacentWidth1 = -outlineWidth;
1288 }
1289 auto bottomRight = outlineBoxRect.minXMaxYCorner();
1290 if (nextLine.isEmpty() || thisLine.x() <= nextLine.x() || (nextLine.maxX()) <= thisLine.x()) {
1291 bottomRight.move(0, outlineWidth);
1292 adjacentWidth2 = outlineWidth;
1293 } else {
1294 bottomRight.move(0, -2 * outlineOffset);
1295 adjacentWidth2 = -outlineWidth;
1296 }
1297 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSLeft, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1298
1299 // right edge
1300 topLeft = outlineBoxRect.maxXMinYCorner();
1301 if (previousLine.isEmpty() || previousLine.maxX() < thisLine.maxX() || thisLine.maxX() <= previousLine.x()) {
1302 topLeft.move(0, -outlineWidth);
1303 adjacentWidth1 = outlineWidth;
1304 } else {
1305 topLeft.move(0, 2 * outlineOffset);
1306 adjacentWidth1 = -outlineWidth;
1307 }
1308 bottomRight = outlineBoxRect.maxXMaxYCorner();
1309 if (nextLine.isEmpty() || nextLine.maxX() <= thisLine.maxX() || thisLine.maxX() <= nextLine.x()) {
1310 bottomRight.move(outlineWidth, outlineWidth);
1311 adjacentWidth2 = outlineWidth;
1312 } else {
1313 bottomRight.move(outlineWidth, -2 * outlineOffset);
1314 adjacentWidth2 = -outlineWidth;
1315 }
1316 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSRight, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1317
1318 // upper edge
1319 if (thisLine.x() < previousLine.x()) {
1320 topLeft = outlineBoxRect.minXMinYCorner();
1321 topLeft.move(-outlineWidth, -outlineWidth);
1322 adjacentWidth1 = outlineWidth;
1323 bottomRight = outlineBoxRect.maxXMinYCorner();
1324 bottomRight.move(outlineWidth, 0);
1325 if (!previousLine.isEmpty() && adjustedPreviousLine.x() < bottomRight.x()) {
1326 bottomRight.setX(adjustedPreviousLine.x() - outlineOffset);
1327 adjacentWidth2 = -outlineWidth;
1328 } else
1329 adjacentWidth2 = outlineWidth;
1330 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1331 }
1332
1333 if (previousLine.maxX() < thisLine.maxX()) {
1334 topLeft = outlineBoxRect.minXMinYCorner();
1335 topLeft.move(-outlineWidth, -outlineWidth);
1336 if (!previousLine.isEmpty() && adjustedPreviousLine.maxX() > topLeft.x()) {
1337 topLeft.setX(adjustedPreviousLine.maxX() + outlineOffset);
1338 adjacentWidth1 = -outlineWidth;
1339 } else
1340 adjacentWidth1 = outlineWidth;
1341 bottomRight = outlineBoxRect.maxXMinYCorner();
1342 bottomRight.move(outlineWidth, 0);
1343 adjacentWidth2 = outlineWidth;
1344 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1345 }
1346
1347 if (thisLine.x() == thisLine.maxX()) {
1348 topLeft = outlineBoxRect.minXMinYCorner();
1349 topLeft.move(-outlineWidth, -outlineWidth);
1350 adjacentWidth1 = outlineWidth;
1351 bottomRight = outlineBoxRect.maxXMinYCorner();
1352 bottomRight.move(outlineWidth, 0);
1353 adjacentWidth2 = outlineWidth;
1354 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1355 }
1356
1357 // lower edge
1358 if (thisLine.x() < nextLine.x()) {
1359 topLeft = outlineBoxRect.minXMaxYCorner();
1360 topLeft.move(-outlineWidth, 0);
1361 adjacentWidth1 = outlineWidth;
1362 bottomRight = outlineBoxRect.maxXMaxYCorner();
1363 bottomRight.move(outlineWidth, outlineWidth);
1364 if (!nextLine.isEmpty() && (adjustedNextLine.x() < bottomRight.x())) {
1365 bottomRight.setX(adjustedNextLine.x() - outlineOffset);
1366 adjacentWidth2 = -outlineWidth;
1367 } else
1368 adjacentWidth2 = outlineWidth;
1369 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1370 }
1371
1372 if (nextLine.maxX() < thisLine.maxX()) {
1373 topLeft = outlineBoxRect.minXMaxYCorner();
1374 topLeft.move(-outlineWidth, 0);
1375 if (!nextLine.isEmpty() && adjustedNextLine.maxX() > topLeft.x()) {
1376 topLeft.setX(adjustedNextLine.maxX() + outlineOffset);
1377 adjacentWidth1 = -outlineWidth;
1378 } else
1379 adjacentWidth1 = outlineWidth;
1380 bottomRight = outlineBoxRect.maxXMaxYCorner();
1381 bottomRight.move(outlineWidth, outlineWidth);
1382 adjacentWidth2 = outlineWidth;
1383 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1384 }
1385
1386 if (thisLine.x() == thisLine.maxX()) {
1387 topLeft = outlineBoxRect.minXMaxYCorner();
1388 topLeft.move(-outlineWidth, 0);
1389 adjacentWidth1 = outlineWidth;
1390 bottomRight = outlineBoxRect.maxXMaxYCorner();
1391 bottomRight.move(outlineWidth, outlineWidth);
1392 adjacentWidth2 = outlineWidth;
1393 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1394 }
1395}
1396
1397#if ENABLE(DASHBOARD_SUPPORT)
1398void RenderInline::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions)
1399{
1400 // Convert the style regions to absolute coordinates.
1401 if (style().visibility() != Visibility::Visible)
1402 return;
1403
1404 const Vector<StyleDashboardRegion>& styleRegions = style().dashboardRegions();
1405 unsigned i, count = styleRegions.size();
1406 for (i = 0; i < count; i++) {
1407 StyleDashboardRegion styleRegion = styleRegions[i];
1408
1409 LayoutRect linesBoundingBox = this->linesBoundingBox();
1410 LayoutUnit w = linesBoundingBox.width();
1411 LayoutUnit h = linesBoundingBox.height();
1412
1413 AnnotatedRegionValue region;
1414 region.label = styleRegion.label;
1415 region.bounds = LayoutRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1416 linesBoundingBox.y() + styleRegion.offset.top().value(),
1417 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1418 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1419 region.type = styleRegion.type;
1420
1421 RenderObject* container = containingBlock();
1422 if (!container)
1423 container = this;
1424
1425 region.clip = container->computeAbsoluteRepaintRect(region.bounds);
1426 if (region.clip.height() < 0) {
1427 region.clip.setHeight(0);
1428 region.clip.setWidth(0);
1429 }
1430
1431 FloatPoint absPos = container->localToAbsolute();
1432 region.bounds.setX(absPos.x() + region.bounds.x());
1433 region.bounds.setY(absPos.y() + region.bounds.y());
1434
1435 regions.append(region);
1436 }
1437}
1438#endif
1439
1440} // namespace WebCore
1441