| 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 | |
| 58 | namespace WebCore { |
| 59 | |
| 60 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderInline); |
| 61 | |
| 62 | RenderInline::RenderInline(Element& element, RenderStyle&& style) |
| 63 | : RenderBoxModelObject(element, WTFMove(style), RenderInlineFlag) |
| 64 | { |
| 65 | setChildrenInline(true); |
| 66 | } |
| 67 | |
| 68 | RenderInline::RenderInline(Document& document, RenderStyle&& style) |
| 69 | : RenderBoxModelObject(document, WTFMove(style), RenderInlineFlag) |
| 70 | { |
| 71 | setChildrenInline(true); |
| 72 | } |
| 73 | |
| 74 | void 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 | |
| 114 | void 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 | |
| 123 | static 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 | |
| 133 | static 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 | |
| 158 | void 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 | |
| 170 | void 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 | |
| 202 | void 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 | |
| 235 | LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, unsigned, LayoutUnit* ) |
| 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 | |
| 259 | void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| 260 | { |
| 261 | m_lineBoxes.paint(this, paintInfo, paintOffset); |
| 262 | } |
| 263 | |
| 264 | template<typename GeneratorContext> |
| 265 | void 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 | |
| 276 | template<typename GeneratorContext> |
| 277 | void 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 | |
| 356 | namespace { |
| 357 | |
| 358 | class AbsoluteRectsGeneratorContext { |
| 359 | public: |
| 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 | } |
| 370 | private: |
| 371 | Vector<LayoutRect>& m_rects; |
| 372 | const LayoutPoint& m_accumulatedOffset; |
| 373 | }; |
| 374 | |
| 375 | } // unnamed namespace |
| 376 | |
| 377 | void 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 | |
| 395 | namespace { |
| 396 | |
| 397 | class AbsoluteQuadsGeneratorContext { |
| 398 | public: |
| 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 | } |
| 410 | private: |
| 411 | Vector<FloatQuad>& m_quads; |
| 412 | RenderGeometryMap m_geometryMap; |
| 413 | }; |
| 414 | |
| 415 | } // unnamed namespace |
| 416 | |
| 417 | void 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) |
| 427 | void RenderInline::absoluteQuadsForSelection(Vector<FloatQuad>& quads) const |
| 428 | { |
| 429 | AbsoluteQuadsGeneratorContext context(this, quads); |
| 430 | generateLineBoxRects(context); |
| 431 | } |
| 432 | #endif |
| 433 | |
| 434 | LayoutUnit RenderInline::offsetLeft() const |
| 435 | { |
| 436 | LayoutPoint topLeft; |
| 437 | if (InlineBox* firstBox = firstLineBoxIncludingCulling()) |
| 438 | topLeft = flooredLayoutPoint(firstBox->topLeft()); |
| 439 | return adjustedPositionRelativeToOffsetParent(topLeft).x(); |
| 440 | } |
| 441 | |
| 442 | LayoutUnit RenderInline::offsetTop() const |
| 443 | { |
| 444 | LayoutPoint topLeft; |
| 445 | if (InlineBox* firstBox = firstLineBoxIncludingCulling()) |
| 446 | topLeft = flooredLayoutPoint(firstBox->topLeft()); |
| 447 | return adjustedPositionRelativeToOffsetParent(topLeft).y(); |
| 448 | } |
| 449 | |
| 450 | static 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 | |
| 461 | LayoutUnit RenderInline::marginLeft() const |
| 462 | { |
| 463 | return computeMargin(this, style().marginLeft()); |
| 464 | } |
| 465 | |
| 466 | LayoutUnit RenderInline::marginRight() const |
| 467 | { |
| 468 | return computeMargin(this, style().marginRight()); |
| 469 | } |
| 470 | |
| 471 | LayoutUnit RenderInline::marginTop() const |
| 472 | { |
| 473 | return computeMargin(this, style().marginTop()); |
| 474 | } |
| 475 | |
| 476 | LayoutUnit RenderInline::marginBottom() const |
| 477 | { |
| 478 | return computeMargin(this, style().marginBottom()); |
| 479 | } |
| 480 | |
| 481 | LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const |
| 482 | { |
| 483 | return computeMargin(this, style().marginStartUsing(otherStyle ? otherStyle : &style())); |
| 484 | } |
| 485 | |
| 486 | LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const |
| 487 | { |
| 488 | return computeMargin(this, style().marginEndUsing(otherStyle ? otherStyle : &style())); |
| 489 | } |
| 490 | |
| 491 | LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const |
| 492 | { |
| 493 | return computeMargin(this, style().marginBeforeUsing(otherStyle ? otherStyle : &style())); |
| 494 | } |
| 495 | |
| 496 | LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const |
| 497 | { |
| 498 | return computeMargin(this, style().marginAfterUsing(otherStyle ? otherStyle : &style())); |
| 499 | } |
| 500 | |
| 501 | const 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 | |
| 515 | bool 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 | |
| 521 | namespace { |
| 522 | |
| 523 | class HitTestCulledInlinesGeneratorContext { |
| 524 | public: |
| 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 | |
| 539 | private: |
| 540 | bool m_intersected; |
| 541 | Region& m_region; |
| 542 | const HitTestLocation& m_location; |
| 543 | }; |
| 544 | |
| 545 | } // unnamed namespace |
| 546 | |
| 547 | bool 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 | |
| 569 | VisiblePosition 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 | |
| 592 | namespace { |
| 593 | |
| 594 | class LinesBoundingBoxGeneratorContext { |
| 595 | public: |
| 596 | LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { } |
| 597 | |
| 598 | void addRect(const FloatRect& rect) |
| 599 | { |
| 600 | m_rect.uniteIfNonZero(rect); |
| 601 | } |
| 602 | private: |
| 603 | FloatRect& m_rect; |
| 604 | }; |
| 605 | |
| 606 | } // unnamed namespace |
| 607 | |
| 608 | IntRect 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 | |
| 647 | InlineBox* 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 | |
| 676 | InlineBox* 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 | |
| 705 | LayoutRect 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 | |
| 746 | LayoutRect 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 | |
| 775 | LayoutRect 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 | |
| 819 | LayoutRect 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 | |
| 867 | LayoutRect 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 | |
| 875 | LayoutRect 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 | |
| 887 | Optional<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 | |
| 935 | LayoutSize 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 | |
| 952 | void 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 | |
| 1000 | const 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 | |
| 1033 | void RenderInline::updateDragState(bool dragOn) |
| 1034 | { |
| 1035 | RenderBoxModelObject::updateDragState(dragOn); |
| 1036 | if (RenderBoxModelObject* continuation = this->continuation()) |
| 1037 | continuation->updateDragState(dragOn); |
| 1038 | } |
| 1039 | |
| 1040 | void 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 | |
| 1064 | void 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 | |
| 1100 | void RenderInline::deleteLines() |
| 1101 | { |
| 1102 | m_lineBoxes.deleteLineBoxTree(); |
| 1103 | } |
| 1104 | |
| 1105 | std::unique_ptr<InlineFlowBox> RenderInline::createInlineFlowBox() |
| 1106 | { |
| 1107 | return std::make_unique<InlineFlowBox>(*this); |
| 1108 | } |
| 1109 | |
| 1110 | InlineFlowBox* 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 | |
| 1119 | LayoutUnit 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 | |
| 1130 | int 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 | |
| 1137 | LayoutSize 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 | |
| 1177 | void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) |
| 1178 | { |
| 1179 | if (!parent()) |
| 1180 | return; |
| 1181 | |
| 1182 | // FIXME: We can do better. |
| 1183 | repaint(); |
| 1184 | } |
| 1185 | |
| 1186 | void 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 | |
| 1211 | void 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 | |
| 1258 | void 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) |
| 1398 | void 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 | |